题解 P2664 【树上游戏】
Salamander · · 题解
考虑点分治,对于当前分治中心,统计出它自己出发到分治块内的所有路径对自己答案的贡献,和经过它的路径对当前分治块内点的贡献。自己出发到分治块内的所有路径对自己答案的贡献很好求,现在考虑怎么求经过它的路径对当前分治块内点的贡献。
我们对于当前分治中心的每一个子树分别考虑,令
对于一个出现在分治中心到
所以只需要对于当前分治中心求出
复杂度
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define For(i,_beg,_end) for(int i=(_beg),i##end=(_end);i<=i##end;++i)
#define Rep(i,_beg,_end) for(int i=(_beg),i##end=(_end);i>=i##end;--i)
template<typename T>T Max(const T &x,const T &y){return x<y?y:x;}
template<typename T>T Min(const T &x,const T &y){return x<y?x:y;}
template<typename T>int chkmax(T &x,const T &y){return x<y?(x=y,1):0;}
template<typename T>int chkmin(T &x,const T &y){return x>y?(x=y,1):0;}
template<typename T>void read(T &x){
T f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(x=0;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
x*=f;
}
typedef long long LL;
const int maxn=100010,inf=0x7fffffff;
struct edge{
int to,nxt;
}e[maxn<<1];
int n,num,head[maxn],c[maxn];
int f[maxn],size[maxn],Size,root;
int has[maxn],col[maxn],top,cl[maxn],tp;
LL sum[maxn],res[maxn],cnt[maxn],ct[maxn],cct[maxn],path,tot;
bool vis[maxn],exist[maxn];
void addedge(int,int);
void get_sum(int,int);//计算子树大小
void get_root(int,int);//计算重心
void Dfs(int,int,LL*);//统计哪些颜色出现过,同时计算cnt数组
void Modify(int,int,LL);//更新子树的答案
void Calc(int);//对于分治中心统计答案
void Solve(int);//点分治
int main(){
read(n);
For(i,1,n) read(c[i]);
For(i,1,n-1){
int u,v;
read(u);read(v);
addedge(u,v);
}
Solve(1);
For(i,1,n) printf("%lld\n",sum[i]);
return 0;
}
void Calc(int x){
get_sum(x,Size=0);
tot=top=0;
Dfs(x,0,cnt);
For(i,1,top) exist[col[i]]=false;//要及时还原标记数组
For(i,1,tp=top){
tot+=cnt[cl[i]=col[i]];
cct[col[i]]=cnt[col[i]];
}
sum[x]+=tot;
LL temp=tot;
for(int i=head[x];i;i=e[i].nxt) if(!vis[e[i].to]){
has[c[x]]=1;top=0;
Dfs(e[i].to,x,ct);
has[c[x]]=0;
For(j,1,top) exist[col[j]]=false;
cnt[c[x]]-=size[e[i].to];
tot-=size[e[i].to];
For(j,1,top){
cnt[col[j]]-=ct[col[j]];
tot-=ct[col[j]];
}
path=size[x]-size[e[i].to];
Modify(e[i].to,x,0);
cnt[c[x]]+=size[e[i].to];
tot=temp;
For(j,1,top){
cnt[col[j]]=cct[col[j]];
ct[col[j]]=0;
}
}
For(i,1,tp) cnt[cl[i]]=0;//还原数组
vis[x]=true;
}
void Modify(int x,int fa,LL lst){//lst为之前下传下来的贡献和
LL tag=lst;
if(++has[c[x]]==1) tag+=path-cnt[c[x]];
sum[x]+=tot+tag;
for(int i=head[x];i;i=e[i].nxt){
if(e[i].to==fa||vis[e[i].to]) continue;
Modify(e[i].to,x,tag);
}
has[c[x]]--;
}
void Dfs(int x,int fa,LL *cnt){
if(!exist[c[x]]) col[++top]=c[x],exist[c[x]]=true;
if(++has[c[x]]==1) cnt[c[x]]+=size[x];
for(int i=head[x];i;i=e[i].nxt){
if(e[i].to==fa||vis[e[i].to]) continue;
Dfs(e[i].to,x,cnt);
}
has[c[x]]--;
}
void Solve(int x){
Calc(x);
for(int i=head[x];i;i=e[i].nxt) if(!vis[e[i].to]){
f[Size=root=0]=inf;
get_sum(e[i].to,x);
get_root(e[i].to,x);
Solve(root);
}
}
void get_sum(int x,int fa){
Size++;size[x]=1;
for(int i=head[x];i;i=e[i].nxt){
if(e[i].to==fa||vis[e[i].to]) continue;
get_sum(e[i].to,x);
size[x]+=size[e[i].to];
}
}
void get_root(int x,int fa){
f[x]=0;
for(int i=head[x];i;i=e[i].nxt){
if(e[i].to==fa||vis[e[i].to]) continue;
get_root(e[i].to,x);
chkmax(f[x],size[e[i].to]);
}
chkmax(f[x],Size-size[x]);
if(f[x]<f[root]) root=x;
}
void addedge(int u,int v){
e[++num].to=v;e[num].nxt=head[u];head[u]=num;
e[++num].to=u;e[num].nxt=head[v];head[v]=num;
}