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;
}

  

原文地址:https://www.cnblogs.com/FrankChen831X/p/11389459.html