zoukankan      html  css  js  c++  java
  • dsu on tree 与长链剖分

    dsu on tree

    对于树进行轻重链剖分,对于节点 $x$ ,递归所有轻儿子后消除其影响,递归重儿子,不消除其影响。

    然后对于所有轻儿子的子树暴力,从而得到 $x$ 的答案。

    对于要消除暴力消除即可。

    可以发现如果暴力到点 $u$ 必然是其 $u$ 到根的轻边数量,从而时间复杂度除在统计每个节点答案时其余时间复杂度为 $O(nlog n)$ 。

    CF 600E Lomsat gelral

    模板题,按上述过程模拟即可。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define int long long
    using namespace std;
    inline int read(){
        int f=1,ans=0;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return ans*f;
    }
    const int MAXN=100001;
    struct node{
        int u,v,nex;
    }x[MAXN<<1];
    int head[MAXN],cnt,N,A[MAXN];
    void add(int u,int v){
        x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++;
    }
    int Cnt[MAXN],Mx,sum,Ans[MAXN],V,siz[MAXN],son[MAXN];
    void dfs(int u,int fath){
        siz[u]=1;
        for(int i=head[u];i!=-1;i=x[i].nex){
            if(x[i].v==fath) continue;
            dfs(x[i].v,u);siz[u]+=siz[x[i].v];
            if(siz[son[u]]<siz[x[i].v]) son[u]=x[i].v;
        }return;
    }
    void dfs1(int u,int fath,int w){
        Cnt[A[u]]+=w;
        if(Cnt[A[u]]>Mx) Mx=Cnt[A[u]],sum=A[u];
        else if(Cnt[A[u]]==Mx) sum+=A[u];
        for(int i=head[u];i!=-1;i=x[i].nex){
            if(x[i].v==fath||x[i].v==V) continue;
            dfs1(x[i].v,u,w);
        }return;
    }
    void dfs(int u,int fath,int opt){
        for(int i=head[u];i!=-1;i=x[i].nex){
            if(x[i].v==fath||x[i].v==son[u]) continue;
            dfs(x[i].v,u,0);
        }
        if(son[u]) dfs(son[u],u,1);
        V=son[u];dfs1(u,fath,1);
        Ans[u]=sum;
        if(!opt) V=0,dfs1(u,fath,-1),Mx=sum=0;
    }
    signed main(){
        memset(head,-1,sizeof(head));
        N=read();
        for(int i=1;i<=N;i++) A[i]=read();
        for(int i=1;i<N;i++){
            int u=read(),v=read();
            add(u,v),add(v,u);
        }
        dfs(1,0);dfs(1,0,0);
        for(int i=1;i<=N;i++) printf("%lld ",Ans[i]);printf("
    ");
        return 0;
    }
    View Code

    或者可以线段树合并,利用线段树维护颜色个数。

    CF 1009F Dominant Indices

    可以长链剖分也可以 $dsu$ ,$dsu$ 的时间复杂度 $O(nlog n)$ 。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    inline int read(){
        int f=1,ans=0;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
        return f*ans;
    }
    const int MAXN=1000001;
    struct node{
        int u,v,nex;
    }x[MAXN<<1];
    int dep[MAXN],cnt,siz[MAXN],son[MAXN],N,head[MAXN];
    int Ans[MAXN];
    void add(int u,int v){
        x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++;
    }
    void dfs0(int u,int fath){
        siz[u]=1;dep[u]=dep[fath]+1;
        for(int i=head[u];i!=-1;i=x[i].nex){
            if(x[i].v==fath) continue;
            dfs0(x[i].v,u);siz[u]+=siz[x[i].v];
            if(siz[son[u]]<siz[x[i].v]) son[u]=x[i].v;
        }return;
    }
    int Num[MAXN],Mx,Sum,Lim;
    void Add(int u,int fath,int w){ 
        Num[dep[u]]+=w;
        if(Num[dep[u]]>Mx) Mx=Num[dep[u]],Sum=dep[u];
        else if(Num[dep[u]]==Mx&&dep[u]<Sum) Sum=dep[u];
        for(int i=head[u];i!=-1;i=x[i].nex){
            if(x[i].v==fath||x[i].v==Lim) continue;
            Add(x[i].v,u,w);
        }return;
    }
    void dfs1(int u,int fath,int opt){
    //    cerr<<u<<" "<<fath<<" "<<opt<<endl;
        for(int i=head[u];i!=-1;i=x[i].nex){
            if(x[i].v==fath||x[i].v==son[u]) continue;
            dfs1(x[i].v,u,0);
        }
        if(son[u]) dfs1(son[u],u,1);Lim=son[u];
        Add(u,fath,1);Ans[u]=Sum;
        Lim=0;
        if(!opt) Add(u,fath,-1),Mx=Sum=0;
        return;
    }
    int main(){
    //    freopen("maker.in","r",stdin);
        memset(head,-1,sizeof(head));
        N=read();
        for(int i=1;i<N;i++){int u=read(),v=read();add(u,v),add(v,u);}
        dfs0(1,0);dfs1(1,0,0);
        for(int i=1;i<=N;i++){
            printf("%d
    ",Ans[i]-dep[i]);
        }
        return 0;
    }/*
    8
    1 2
    2 3
    1 4
    3 5
    4 6
    5 7
    4 8
    */
    View Code

    长链剖分

    对于重儿子为 $u$ 下面最深的链所在儿子,可以发现最多到根有 $sqrt{n}$ 个长链与短链,因为对于每次走到轻边必加上比他深的儿子,可以写成 $1+2+…x=n->x=sqrt{n}$ 。

    如果一个子树 $dp$ 只与深度有关,则可能可以使用长链剖分的方法优化它的复杂度。

    详细情况请参考 $link$ 。考虑对于继承每个重儿子的话可以用指针维护,或者数组映射即可。

    CF 1009F Dominant Indices

    虽然可以 $dsu$ ,但是通过长链剖分可以得到更优的复杂度 $O(n)$ 。时间复杂度为 $O(n)$ 因为每条重链只统计一次。

    「POI2014」酒店 Hotel

    $nleq 10^5$ 。考虑 $O(n^2)$ 的树形 $dp$ ,设 $f_{i,j}$ 表示在以 $i$ 为根的子树下到 $i$ 距离为 $j$ 的点的个数,$g_{i,j}$ 表示在以 $i$ 为根的子树上有多少个点对需要经过 $i$ 号点后再走 $j$ 步。

    转移考虑当前子树对另一颗子树的贡献与自己的贡献即可。可以发现 $dp$ 的第二维只与深度有关,并且支持合并,长链剖分即可。

    一个小建议就是空间可以多开一点。


    [WC2010] 重建计划

    可以发现将答案二分以后分数规划问题就转换成边数在 $[l,r]$ ,且边权和大于等于 $0$ 是否有解。

    考虑将点对答案在 $lca$ 处处理,维护 $f_{i,j}$ 表示以 $i$ 为根的子树下到 $i$ 经过 $j$ 条边的最大边权。而需要做的是 $f$ 一段区间的 $max$ 。

    很显然 $f$ 数组支持长链剖分,而 $max$ 操作无法通过指针维护,考虑利用数组映射,同时建线段树维护极值。

  • 相关阅读:
    LintCode "Binary Tree Serialization"
    LeetCode "Find the Duplicate Number"
    LintCode "Route Between Two Nodes in Graph"
    LintCode "Search a 2D Matrix II"
    LintCode "Submatrix Sum"
    LintCode "Sort Letters by Case"
    LeetCode "Peeking Iterator"
    LintCode "Sort Colors II"
    LeetCode "Move Zeroes"
    LintCode "Update Bits"
  • 原文地址:https://www.cnblogs.com/si-rui-yang/p/12045828.html
Copyright © 2011-2022 走看看