zoukankan      html  css  js  c++  java
  • luoguP2664树上游戏(点分治)

    题目链接:https://www.luogu.org/problem/P2664

    题意:给定一颗带点权的树,结点数n<=1e5,点权<=1e5,用s(i,j)表示从i到j的路径上不同点权数,ans[i]=sum(s(i,j))。求ans数组。

    思路:

      继续肝淀粉质,太难了。

      涉及到树上点对,且nlogn满足时,就可以考虑考虑点分治了。所以回到这题,我们需要在O(n)时间内统计出以p为根节点的子树上所有节点对p的贡献,以及对所有经过p的路径的贡献。

      

      我们通过dfs1得到这些贡献值和他们的和sum,显然子树上所有节点对p的贡献就是sum,即ans[p]+=sum。

      

      另外,sum、ans数组和color数组需要用LL,在这wa了两发。

    AC代码:

    #include<cstdio>
    #include<algorithm>
    #include<cctype>
    using namespace std;
    
    inline int read(){
        int x=0,f=0;char c=0;
        while(!isdigit(c)){f|=c=='-';c=getchar();}
        while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return f?-x:x;
    }
    
    typedef long long LL;
    const int maxn=1e5+5;
    const int inf=0x3f3f3f3f;
    struct node{
        int v,nex;
    }edge[maxn<<1];
    
    int n,c[maxn],ct,head[maxn],Min,root,size,sz[maxn],mson[maxn];
    int cnt[maxn],num,t,vis[maxn];
    LL ans[maxn],color[maxn],sum;
    
    void adde(int u,int v){
        edge[++ct].v=v;
        edge[ct].nex=head[u];
        head[u]=ct;
    }
    
    void getroot(int u,int fa){
        sz[u]=1,mson[u]=0;
        for(int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==fa||vis[v]) continue;
            getroot(v,u);
            sz[u]+=sz[v];
            if(sz[v]>mson[u]) mson[u]=sz[v];
        }
        if(size-sz[u]>mson[u]) mson[u]=size-sz[u];
        if(mson[u]<Min) Min=mson[u],root=u;
    }
    
    void dfs1(int u,int fa){
        sz[u]=1;
        ++cnt[c[u]];
        for(int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==fa||vis[v]) continue;
            dfs1(v,u);
            sz[u]+=sz[v];
        }
        if(cnt[c[u]]==1){
            sum+=sz[u];
            color[c[u]]+=sz[u];
        }
        --cnt[c[u]];
    }
    
    void change(int u,int fa,int f){
        ++cnt[c[u]];
        for(int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==fa||vis[v]) continue;
            change(v,u,f);
        }
        if(cnt[c[u]]==1){
            sum+=sz[u]*f;
            color[c[u]]+=sz[u]*f;
        }
        --cnt[c[u]];
    }
    
    void dfs2(int u,int fa){
        ++cnt[c[u]];
        if(cnt[c[u]]==1){
            sum-=color[c[u]];
            ++num;
        }
        ans[u]+=sum+num*t;
        for(int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if(vis[v]||v==fa) continue;
            dfs2(v,u);
        }
        if(cnt[c[u]]==1){
            sum+=color[c[u]];
            --num;
        }
        --cnt[c[u]];
    }
    
    void clear(int u,int fa){
        cnt[c[u]]=color[c[u]]=0;
        for(int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if(vis[v]||v==fa) continue;
            clear(v,u);
        }
    }
    
    void solve(int u){
        dfs1(u,0);
        ans[u]+=sum;
        for(int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if(vis[v]) continue;
            ++cnt[c[u]],sum-=sz[v],color[c[u]]-=sz[v];
            change(v,u,-1);--cnt[c[u]];
            t=sz[u]-sz[v];
            dfs2(v,u);
            ++cnt[c[u]],sum+=sz[v],color[c[u]]+=sz[v];
            change(v,u,1);--cnt[c[u]];
        }
        sum=0,num=0;
        clear(u,0);
    }
    
    void fenzhi(int u){
        vis[u]=1;
        solve(u);
        for(int i=head[u];i;i=edge[i].nex){
            int v=edge[i].v;
            if(vis[v]) continue;
            Min=inf,root=0,size=sz[v];
            getroot(v,0);
            fenzhi(root);
        }
    }
    
    int main(){
        n=read();
        for(int i=1;i<=n;++i)
            c[i]=read();
        for(int i=1;i<n;++i){
            int u=read(),v=read();
            adde(u,v);
            adde(v,u);
        }
        Min=inf,root=0,size=n;
        getroot(1,0);
        fenzhi(root);
        for(int i=1;i<=n;++i)
            printf("%lld
    ",ans[i]);
        return 0;
    }

      

  • 相关阅读:
    二分和牛顿法实现开根号
    leetcode 44. 通配符匹配
    leetcode 91. 解码方法
    leetcode 236. 二叉树的最近公共祖先
    leetcode 39. 组合总数
    leetcode:146. LRU缓存机制
    leetcode:124. 二叉树中的最大路径和
    二叉树前序遍历,中序遍历,后序遍历以及层次遍历实现
    leetcode: 4. 寻找两个有序数组的中位数
    Leetcode: 43. 接雨水
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/11389459.html
Copyright © 2011-2022 走看看