Lomsat gelral(树上启发式合并)

例题

轻重链剖分暴力。主要是skip数组有点蒙。

对于一个点A,一定是先把A的轻儿子先全部搜完然后搜A的重儿子。

轻儿子搜完之后记录都清空了。最后搜重儿子的时候搜完了要保留。if(son[u]) dfs(son[u],v,1),skip[son[u]]=1;通过skip保留。

然后是统计整个A的子树,这个时候calc就会把A的重儿子跳过。calc完了之后就得到ans[A]。

然后把A的重儿子的skip标记取消,这保证了:如果A是一个轻儿子,它整个子树的贡献都被清空。

我靠,要用long long。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int maxm=2e5+10;
int col[maxn],cnt[maxn];
int Head[maxn],Next[maxm],V[maxm],Cnt=1;
int son[maxn],sz[maxn],skip[maxn];
int n,x,y,mx;
long long sum,ans[maxn];
inline int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
inline void print(long long x){
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
inline void add(int u,int v){
	++Cnt;
	Next[Cnt]=Head[u];
	V[Cnt]=v;
	Head[u]=Cnt;
}
void getson(int u,int f){
	sz[u]=1,son[u]=0;
	for(int i=Head[u];i;i=Next[i]){
		if(V[i]==f) continue;
		getson(V[i],u);
		sz[u]+=sz[V[i]];
		if(sz[son[u]]<sz[V[i]]) son[u]=V[i];
	}
}
void calc(int u,int f,int val){
	cnt[col[u]]+=val;
	if(cnt[col[u]]>mx) sum=col[u],mx=cnt[col[u]];
	else if(cnt[col[u]]==mx) sum+=col[u];
	for(int i=Head[u];i;i=Next[i]){
		if(V[i]==f||skip[V[i]]) continue;
		calc(V[i],u,val);
	}
}
void dfs(int u,int f,int keep){
	int v;
	for(int i=Head[u];i;i=Next[i]){
		v=V[i];if(v==f||v==son[u]) continue;
		dfs(v,u,0);
	}
	if(son[u]) dfs(son[u],u,1),skip[son[u]]=1;
	calc(u,f,1),ans[u]=sum,skip[son[u]]=0;
	if(!keep) calc(u,f,-1),sum=0,mx=0;
}
int main(){
	n=read();
	for(int i=1;i<=n;++i) col[i]=read();
	for(int i=1;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
	getson(1,0),dfs(1,0,1);
	for(int i=1;i<=n;++i) print(ans[i]),putchar(' ');
}

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/85344674