题意:
一棵树,每个点都有一个字母,如果一条路径的字符的某个排列是回文串,就称这个路径是回文的,问经过每个点的回文路径有多少条
思路:
很明显的点分治。首先因为字母是a到t,所以可以状压一下,利用异或来改变路径的状态,那么任意一个路径都可以用一个值来表示,如果一个值的二进制中1的个数小于等于1,那么这个路径就是回文的(利用__builtin_popcount函数)。
然后根据树分治的思想,我们选取了一个重心,我们先把以重心为根,所有子树的所有路径通过状压后的值用一个数组记录数量。我们这里要做的,就是统计通过某个点的回文路径有多少条。当我们要算某个子树的贡献时,我们就先删去这个子树的所有的路径的值的贡献,然后统计答案,统计完了再把这些值放回去。
但是存在一个问题,就是当我们发现dfs到某个点的时候,路径构成的值可以构成一个回文路径时,需要把重心到这个点的路径上的所有点的答案都加上一个值。这里可以通过dfs的时候顺便记录子树的贡献,记录完后把值返还给父亲即可。
剩下的就是一些小细节了(其实细节还蛮多的),写的时候注意一下即可
错误及反思:
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 200100;
struct EDGE{
int to,next;
}e[N*2];
int first[N],n,tot,si[N],maxn[N],k;
long long ans[N];
bool did[N];
char s[N];
long long m[2000000];
vector<int> v;
void addedge(int x,int y){
e[tot].to=y;
e[tot].next=first[x];
first[x]=tot++;
e[tot].to=x;
e[tot].next=first[y];
first[y]=tot++;
}
void dfs_size(int now,int fa){
si[now]=1;
maxn[now]=0;
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].to!=fa&&!did[e[i].to]){
dfs_size(e[i].to,now);
si[now]+=si[e[i].to];
maxn[now]=max(maxn[now],si[e[i].to]);
}
}
void dfs_root(int now,int fa,int& root,int& nu,int t){
int MA=max(maxn[now],si[t]-si[now]);
if(MA<nu){
nu=MA;
root=now;
}
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].to!=fa&&!did[e[i].to])
dfs_root(e[i].to,now,root,nu,t);
}
void dfs2(int now,int fa,int tlen){
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].to!=fa&&!did[e[i].to])
dfs2(e[i].to,now,tlen^(1<<(s[e[i].to]-'a')));
v.push_back(tlen);
}
long long dfs3(int now,int fa,int tlen,int root){
long long num=0;
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].to!=fa&&!did[e[i].to]){
num+=dfs3(e[i].to,now,tlen^(1<<(s[e[i].to]-'a')),root);
}
ans[now]+=num;
for(int k=0;k<20;k++){
num+=m[(1<<k)^tlen^(1<<(s[root]-'a'))];
ans[now]+=m[(1<<k)^tlen^(1<<(s[root]-'a'))];
}
num+=m[tlen^(1<<(s[root]-'a'))];
ans[now]+=m[tlen^(1<<(s[root]-'a'))];
return num;
}
long long dfs4(int now,int fa,int tlen,int root){
long long num=0;
for(int i=first[now];i!=-1;i=e[i].next)
if(e[i].to!=fa&&!did[e[i].to]){
num+=dfs4(e[i].to,now,tlen^(1<<(s[e[i].to]-'a')),root);
}
if(__builtin_popcount(tlen^(1<<(s[root]-'a')))<=1) num++;
ans[now]+=num;
return num;
}
void solve(int now){
int root,nu=1e9;
dfs_size(now,-1);
dfs_root(now,-1,root,nu,now);
did[root]=true;
for(int i=first[root];i!=-1;i=e[i].next){
if(!did[e[i].to]){
v.clear();
dfs2(e[i].to,root,(1<<(s[e[i].to]-'a')));
for(int j=0;j<v.size();j++) m[v[j]]++;
}
}
long long td=0;
for(int i=first[root];i!=-1;i=e[i].next){
if(!did[e[i].to]){
v.clear();
dfs2(e[i].to,root,(1<<(s[e[i].to]-'a')));
for(int j=0;j<v.size();j++) m[v[j]]--;
td+=dfs3(e[i].to,root,(1<<(s[e[i].to]-'a')),root);
ans[root]+=dfs4(e[i].to,root,(1<<(s[e[i].to]-'a')),root);
for(int j=0;j<v.size();j++) m[v[j]]++;
}
}
ans[root]+=td/2;
for(int i=first[root];i!=-1;i=e[i].next){
if(!did[e[i].to]){
v.clear();
dfs2(e[i].to,root,(1<<(s[e[i].to]-'a')));
for(int j=0;j<v.size();j++) m[v[j]]--;
}
}
for(int i=first[root];i!=-1;i=e[i].next)
if(!did[e[i].to])
solve(e[i].to);
}
void init(){
memset(first,-1,sizeof(first));
tot=0;
}
int main(){
scanf("%d",&n);
init();
for(int i=0,u,v;i<n-1;i++){
scanf("%d%d",&u,&v);
addedge(u,v);
}
scanf("%s",s+1);
solve(1);
for(int i=1;i<=n;i++) printf("%lld ",ans[i]+1);
}