1 条题解
-
1
转化版题意
给你一颗 个顶点的树,顶点从 到 编号,并且每个顶点对应一个在
a到t的字母。 求经过每个节点的至多有一个字母的数量为奇数的路径的个数。注意:从 到 的路径与从 到 的路径视为相同,只计数一次。
分析
a到t有 个字母,于是我们可以状压一下,每一位代表该字母数量是奇是偶,而维护路径的个数不难想到点分治,设 表示当前分治中心 到点 的路径上的字母数量状压后的结果,则一条路径 符合要求仅当对于一条路径,我们把它拆成 两部分,这样我们只要对半边的路径计数即可,具体地,计算 的贡献时,给 上的每一个点加上当前分治中心的全局中 $(d_y=2^j\oplus d_x \oplus d_{pos}) \lor (d_y=0 \oplus d_x \oplus d_{pos})$ 的数量。但这样统计会重复加上当前子树中 $(d_y=2^j\oplus d_x \oplus d_{pos}) \lor (d_y=0 \oplus d_x \oplus d_{pos})$ 的数量,所以再统计当前子树中的数量,提前减去即可。
但是还是有缺陷,首先,路径端点在分治中心的不同子树时分治中心 的答案会被算两次,最后特判下除以 即可;其次,这样的做法只统计了路径端点在分治中心的不同子树时的答案,但还有以分治中心为端点,另一端在子树内的答案和整个路径只有分治中心的答案,对于前者,特判计算即可,对于后者,最后输出答案时加 即可。
但值得注意的是,分治中心的答案除以 时不能把以分治中心为端点的除了,于是要在前面的特判中多记一个变量表示以分治中心为端点的路径的个数,对分治中心的答案加上此再除以 就没问题了。
分治过程中涉及到树上路径增加的操作,用树上差分就可以了。
:该代码的语言为 GNU G++17 9.2.0 (64 bit, msys 2)
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=2e5+10,M=4e5+10,K=2e6+10; inline ll read(){ ll 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; } inline void write(ll x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } string s;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,ans[N],rans[N]; 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; }
- 1
信息
- ID
- 4191
- 时间
- 4000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者