CF914E Palindromes in a Tree
题意
转化版题意。
给你一颗 个顶点的树,顶点从 到 编号,并且每个顶点对应一个在
a到t的字母。 求经过每个节点的至多有一个字母的数量为奇数的路径的个数。注意:从 到 的路径与从 到 的路径视为相同,只计数一次。
分析
a 到 t 有 个字母,于是我们可以状压一下,每一位代表该字母数量是奇是偶,而维护路径的个数不难想到点分治,设 表示当前分治中心 到点 的路径上的字母数量状压后的结果,则一条路径 符合要求仅当 。
对于一条路径,我们把它拆成 两部分,这样我们只要对半边的路径计数即可,具体地,计算 的贡献时,给 上的每一个点加上当前分治中心的全局中 的数量,但这样统计会重复加上当前子树中 的数量,所以再统计当前子树中的数量,提前减去即可。
但是还是有缺陷,首先,路径端点在分治中心的不同子树时分治中心 的答案会被算两次,最后特判下除以 即可;其次,这样的做法只统计了路径端点在分治中心的不同子树时的答案,但还有以分治中心为端点,另一端在子树内的答案和整个路径只有分治中心的答案,对于前者,特判计算即可,对于后者,最后输出答案时加 即可。
但值得注意的是,分治中心的答案除以 时不能把以分治中心为端点的除了,于是要在前面的特判中多记一个变量表示以分治中心为端点的路径的个数,对分治中心的答案加上此再除以 就没问题了。
分治过程中涉及到树上路径增加的操作,用树上差分就可以了。
代码
#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;
}

浙公网安备 33010602011771号