CF914E Palindromes in a Tree

Link\text{Link}

题意

转化版题意。

给你一颗 nn 个顶点的树,顶点从 11nn 编号,并且每个顶点对应一个在 at 的字母。 求经过每个节点的至多有一个字母的数量为奇数的路径的个数。

注意:从 uuvv 的路径与从 vvuu 的路径视为相同,只计数一次。

分析

at2020 个字母,于是我们可以状压一下,每一位代表该字母数量是奇是偶,而维护路径的个数不难想到点分治,设 dxd_x 表示当前分治中心 pospos 到点 xx 的路径上的字母数量状压后的结果,则一条路径 xyx \Leftrightarrow y 符合要求仅当 dxdydpos=02j(0j<20)d_x\oplus d_y \oplus d_{pos}=0\lor2^j(0\le j<20)

对于一条路径,我们把它拆成 xpos,posyx \Leftrightarrow pos,pos \Leftrightarrow y 两部分,这样我们只要对半边的路径计数即可,具体地,计算 xposx \Leftrightarrow pos 的贡献时,给 xposx \Leftrightarrow pos 上的每一个点加上当前分治中心的全局中 (dy=2jdxdpos)(dy=0dxdpos)(d_y=2^j\oplus d_x \oplus d_{pos}) \lor (d_y=0 \oplus d_x \oplus d_{pos}) 的数量,但这样统计会重复加上当前子树中 (dy=2jdxdpos)(dy=0dxdpos)(d_y=2^j\oplus d_x \oplus d_{pos}) \lor (d_y=0 \oplus d_x \oplus d_{pos}) 的数量,所以再统计当前子树中的数量,提前减去即可。

但是还是有缺陷,首先,路径端点在分治中心的不同子树时分治中心 pospos 的答案会被算两次,最后特判下除以 22 即可;其次,这样的做法只统计了路径端点在分治中心的不同子树时的答案,但还有以分治中心为端点,另一端在子树内的答案和整个路径只有分治中心的答案,对于前者,特判计算即可,对于后者,最后输出答案时加 11 即可。

但值得注意的是,分治中心的答案除以 22 时不能把以分治中心为端点的除了,于是要在前面的特判中多记一个变量表示以分治中心为端点的路径的个数,对分治中心的答案加上此再除以 22 就没问题了。

分治过程中涉及到树上路径增加的操作,用树上差分就可以了。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=2e5+10,M=4e5+10,K=1.2e6+10;
int head[M],ver[M],nxt[M],tot,Fa[N];
int n,pos,mx,sze[N],v1[N],d[N],f[K],f2[K],e1[N],e2[N],t1,t2;
ll sum[N],sum2;
ll ans[N],rans[N];
string s;
void add(int x,int y){
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void dfs(int x,int S){
	v1[x]=sze[x]=1;
	int mx_part=0;
	for(int i=head[x];i;i=nxt[i]){
		int y=ver[i];
		if(v1[y]==1||v1[y]==3)
			continue;
		dfs(y,S);
		sze[x]+=sze[y];
		mx_part=max(mx_part,sze[y]);
	}
	mx_part=max(mx_part,S-sze[x]);
	if(mx_part<mx){
		mx=mx_part;
		pos=x;
	}
}
void dfs2(int x,int fa){
	v1[x]=2;
	Fa[x]=fa;
	d[x]=d[fa]^(1<<(s[x-1]-'a'));
	e2[++t2]=x;
	for(int i=head[x];i;i=nxt[i]){
		int y=ver[i];
		if(v1[y]==2||v1[y]==3)
			continue;
		dfs2(y,x);
	}
}
void dfs3(int x,int F){
	v1[x]=0;
	sum[x]=rans[x];
	for(int i=head[x];i;i=nxt[i]){
		int y=ver[i];
		if(!v1[y]||v1[y]==3)
			continue;
		dfs3(y,0);
		sum[x]+=sum[y];
	}
	if(F)
		sum[x]=(sum[x]+sum2)/2;
	ans[x]+=sum[x];
}
void add(int x,int y,int v){
	rans[Fa[x]]-=v;
	rans[y]+=v;
}
void calc(int x){
	t1=sum2=Fa[x]=0;
	v1[x]=2;
	d[x]=(1<<(s[x-1]-'a'));
	for(int i=head[x];i;i=nxt[i]){
		if(v1[ver[i]]==3)
			continue;
		t2=0;
		dfs2(ver[i],x);
		for(int j=1;j<=t2;j++){
			e1[++t1]=e2[j];
			f[d[e2[j]]]++;
			f2[d[e2[j]]]++;
		}
		for(int j=1,a;j<=t2;j++){
			a=0;
			a+=f2[d[e2[j]]^d[x]];
			for(int u=0;u<20;u++)
				a+=f2[d[e2[j]]^(1<<u)^d[x]];
			add(x,e2[j],-a);
			
		}
		for(int j=1;j<=t2;j++)
			f2[d[e2[j]]]--;
	}
	for(int i=1,a;i<=t1;i++){
		a=0;
		a+=f[d[e1[i]]^d[x]];
		if(!d[e1[i]])
			a++,sum2++;
		for(int j=0;j<20;j++){
			a+=f[d[e1[i]]^(1<<j)^d[x]];
			if(d[e1[i]]==(1<<j))
				a++,sum2++;
		}
		add(x,e1[i],a);
	}
	dfs3(x,1);
	rans[0]=v1[x]=0;
	for(int i=1;i<=t1;i++){
		f[d[e1[i]]]--;
		rans[e1[i]]=0;
	}
}
void DFZ(int x,int S){
	mx=1e9;pos=0;
	dfs(x,S);
	int mxpos=pos;
	calc(pos);
	v1[pos]=3;
	for(int i=head[mxpos];i;i=nxt[i]){
		int y=ver[i];
		if(v1[y]==3)
			continue;
		DFZ(y,sze[y]);
	}
}
int main(){
	n=read();
	for(int i=1,u,v;i<n;i++){
		u=read();v=read();
		add(u,v);
		add(v,u);
	}
	cin>>s;
	DFZ(1,n);
	for(int i=1;i<=n;i++){
		write(ans[i]+1);
		putchar(' ');
	}
	return 0;
}
posted @ 2022-01-31 16:24  luckydrawbox  阅读(9)  评论(0)    收藏  举报  来源