• 线段树合并总结


    线段树合并总结

    咕咕咕中

    原理

    复杂度

    两棵线段树合并复杂度即为两棵树公共节点数( imes logn),据说因为实际重合部分一般比较少,一次合并两棵线段树的复杂度近似为(O(logn)),所以合并(n)棵线段树复杂的为(O(nlog_n))

    据说空间复杂度也是(nlogn)

    CF600E Lomsat gelral

    一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。

    权值线段树,下标为颜色,维护区间答案和最多颜色编号和,然后自底向上合并即可。

    #include <cstdio>
    #define MAXN 100010
    #define sl tre[x].l
    #define sr tre[x].r
    #define ll long long
    using namespace std;
    int head[MAXN],vv[MAXN*2],nxt[MAXN*2],tot;
    inline void add_edge(int u, int v){
        vv[++tot]=v;
        nxt[tot]=head[u];
        head[u]=tot;
    }
    struct nod{
        int l, r;
        ll ans, sum;
    }tre[MAXN*2*20];
    int rt[MAXN],cnt,n;
    int col[MAXN];
    ll res[MAXN];
    void push_up(int x){
        if(tre[sl].sum>tre[sr].sum){
            tre[x].sum=tre[sl].sum;
            tre[x].ans=tre[sl].ans;
        }else if(tre[sl].sum<tre[sr].sum){
            tre[x].sum=tre[sr].sum;
            tre[x].ans=tre[sr].ans;
        }else{
            tre[x].sum=tre[sl].sum;
            tre[x].ans=tre[sl].ans+tre[sr].ans;
        }
    }
    int merge(int a, int b, int l, int r){
        if(a==0) return b;
        if(b==0) return a;
        if(l==r){
            tre[a].ans=l;
            tre[a].sum+=tre[b].sum;
            return a;
        }
        int mid=(l+r)>>1;
        tre[a].l=merge(tre[a].l, tre[b].l, l, mid);
        tre[a].r=merge(tre[a].r, tre[b].r, mid+1, r);
        push_up(a);
        return a;
    }
    void update(int &cur, int l, int r, int pos, int val){
        if(cur==0) cur=++cnt;
        if(l==r){
            tre[cur].sum+=(ll)val;
            tre[cur].ans=l;
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid) update(tre[cur].l, l, mid, pos, val);
        else update(tre[cur].r, mid+1, r, pos, val);
        push_up(cur);
    }
    void dfs(int u, int f){
        for(int i=head[u];i;i=nxt[i]){
            int v=vv[i];
            if(v==f) continue;
            dfs(v, u);
            merge(rt[u], rt[v], 1, 100000);
        }
        update(rt[u], 1, 100000, col[u], 1);
        res[u]=tre[rt[u]].ans;
    }
    int main(){
        scanf("%d", &n);
        cnt=n;
        for(int i=1;i<=n;++i)
            scanf("%d", &col[i]),rt[i]=i;
        for(int i=1;i<=n-1;++i){
            int u,v;
            scanf("%d %d", &u, &v);
            add_edge(u, v);
            add_edge(v, u);
        }
        dfs(1, 0);
        for(int i=1;i<=n;++i) printf("%lld ", res[i]);
        return 0;
    }
    

    [湖南集训]谈笑风生

    给一棵树(n)个节点,(q)次询问,每次给定(p,k),问有多少三元组((p,b,c))满足(p,b)均为(c)的父亲,(p,b)在树上的距离不超过(k)
    (n,qle 10^5)

    两种情况:

    • (b)(a)的上面,我们发现(b)(min(dep[a]-1, k))个可选位置,而(a)的子树内所有节点(除了节点(a)本身)均可作为(c),所以共有$min(dep[a]-1, k) imes(sz[a]-1) $个三元组满足。
    • 对于(b)(a)的下面,我们可以每个节点都维护一颗下标为深度的权值线段树,因为节点(u)作为(b)的同时,其子树除(u)外均可作为(c),所以对于深度(dep[u])的贡献为(sz[u]-1),然后在(dfs)回溯时同时合并子树线段树,每次答案即为深度区间([dep[a]+1, dep[a]+k])的区间和。
    #include <cstdio>
    #include <algorithm>
    #define MAXN 300003
    #define MAXM 300003*30
    #define ll long long
    using namespace std;
    int head[MAXN],nxt[MAXN*2],vv[MAXN*2],tot;
    inline void add_edge(int u, int v){
        vv[++tot]=v;
        nxt[tot]=head[u];
        head[u]=tot;
    }
    int cnt;
    ll tre[MAXM*2];
    int sl[MAXM*2],sr[MAXM*2];
    void change(int &x, int l, int r, int pos, int val){
        if(x==0) x=++cnt;
        tre[x]+=val;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(pos<=mid) change(sl[x], l, mid, pos, val);
        else change(sr[x], mid+1, r, pos, val);
    }
    ll query(int x, int l, int r, int ql, int qr){
        if(x==0) return 0;
        if(ql<=l&&r<=qr) return tre[x];
        int mid=(l+r)>>1;
        ll res=0;
        if(ql<=mid) res+=query(sl[x], l, mid, ql, qr);
        if(mid<qr) res+=query(sr[x], mid+1, r, ql, qr);
        return res;
    }
    int merge(int a, int b, int l, int r){
        if(a==0||b==0) return a+b;
        int mid=(l+r)>>1;
        int x=++cnt;
        tre[x]=tre[a]+tre[b];
        sl[x]=merge(sl[a], sl[b], l, mid);
        sr[x]=merge(sr[a], sr[b], mid+1, r);
        return x;
    }
    int n,q;
    int sz[MAXN],rot[MAXN],dep[MAXN];
    void dfs(int u, int fa){
        sz[u]=1;
        dep[u]=dep[fa]+1;
        for(int i=head[u];i;i=nxt[i]){
            int v=vv[i];
            if(v==fa) continue;
            dfs(v, u);
            sz[u]+=sz[v];
        }
        change(rot[u], 1, n, dep[u], sz[u]-1);
        rot[fa]=merge(rot[fa], rot[u], 1, n);
    }
    int main(){
        scanf("%d %d", &n, &q);
        for(int i=1;i<n;++i){
            int a,b;scanf("%d %d", &a, &b);
            add_edge(a, b);add_edge(b, a);
        }
        dfs(1, 0);
        while(q--){
            int p,k;scanf("%d %d", &p, &k);
            printf("%lld
    ", query(rot[p], 1, n, dep[p]+1, dep[p]+k)+(ll)(sz[p]-1)*min(k, dep[p]-1));
        }
        return 0;
    }
    

    天天爱跑步

    Peaks 线段树合并

    [HNOI2012]永无乡 线段树合并

    P4556 雨天的尾巴 线段树合并

  • 相关阅读:
    课程设计——五子棋(201521123038)
    JAVA课程设计——单机版五子棋
    201521123038 《Java程序设计》 第十四周学习总结
    201521123038 《Java程序设计》 第十三周学习总结
    网络15软工个人作业5——软件工程总结
    201521123035-个人作业4——alpha阶段个人总结
    201521123035个人作业3
    201521123035结对编程
    201521123035软工阅读作业2
    软件工程个人阅读作业1
  • 原文地址:https://www.cnblogs.com/santiego/p/11603337.html
Copyright © 2020-2023  润新知