题目链接: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; }