题目大意
定义
a
n
s
u
=
∑
i
∈
d
e
c
u
∑
j
∈
d
e
c
u
,
i
<
j
(
v
a
l
i
⊕
v
a
l
j
)
×
w
(
S
i
,
S
j
)
ans_u=\sum_{i \in dec_u}\sum_{j \in dec_u,i<j}(val_i \oplus val_j)\times w(S_i,S_j)
ansu=i∈decu∑j∈decu,i<j∑(vali⊕valj)×w(Si,Sj)
其中
⊕
\oplus
⊕为异或,
d
e
c
u
dec_u
decu表示
u
u
u的子树,
w
(
S
i
,
S
j
)
w(S_i,S_j)
w(Si,Sj)表示字符串
S
i
S_i
Si和
S
j
S_j
Sj的最大公共前缀
求树上每个点对应的 a n s u ans_u ansu
TJ
前置知识
w ( S i , S j ) w(S_i , S_j) w(Si,Sj)显然可以用Trie维护,异或操作可以按位拆开计算答案。
方法一
考虑对每个点维护一棵Trie,于是将儿子的Trie合并时就可以计算答案
方法二
树上启发式合并
只维护一棵Trie,每到一个节点先递归计算其轻儿子的答案,计算前后要清空Trie,然后计算重儿子的答案,保留Trie,再将其他儿子的节点一一插入Trie中并计算答案即可
#include<bits/stdc++.h>
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define fd(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
const ll INF=1e9+7;
using namespace std;
const ll N=1e5;
const ll SZ=5e5;
const ll M=20;
char str[SZ+10];
ll l[N+10],v[N+10];
namespace TRIE{
int ch[SZ+10][30],s[SZ+10][30],a[SZ+10],cnt;
void init(ll t){
memset(ch[t],0,sizeof(ch[t]));
memset(s[t],0,sizeof(s[t]));
a[t]=0;
}
ll add(ll t){
ll x=1,sum=0;
fo(i,l[t],l[t+1]-1){
if(!ch[x][str[i]-'a']){
ch[x][str[i]-'a']=++cnt;
init(cnt);
}
x=ch[x][str[i]-'a'];
fo(j,0,M){
if((1<<j)&v[t]){
sum+=(a[x]-s[x][j])*(1ll<<j);
}else{
sum+=s[x][j]*(1ll<<j);
}
}
a[x]++;
fo(j,0,M){
if((1ll<<j)&v[t])s[x][j]++;
}
}
return sum;
}
}
struct edge{
ll st,en,next;
}E[N*2+10];
ll n,tot,last[N+10],X,Y,fa[N+10],son[N+10],size[N+10];
ll len,ans[N+10],cnt,t[N+10],dfn[N+10];
void add(ll x,ll y){
E[++tot]=(edge){x,y,last[x]};
last[x]=tot;
}
void dfs1(ll x){
dfn[x]=++cnt;
t[cnt]=x;
size[x]=1;
for(ll p=last[x];p;p=E[p].next){
ll y=E[p].en;
if(y==fa[x])continue;
fa[y]=x;
dfs1(y);
size[x]+=size[y];
if(size[y]>size[son[x]])son[x]=y;
}
}
void dfs2(ll x){
for(ll p=last[x];p;p=E[p].next){
ll y=E[p].en;
if(y==fa[x] || y==son[x])continue;
TRIE::cnt=1;
TRIE::init(1);
dfs2(y);
// ans[x]+=ans[y];
}
TRIE::cnt=1;
TRIE::init(1);
if(son[x]){
dfs2(son[x]);
ans[x]+=ans[son[x]];
for(ll p=last[x];p;p=E[p].next){
ll y=E[p].en;
if(y==fa[x] || y==son[x])continue;
fo(i,dfn[y],dfn[y]+size[y]-1){
ans[x]+=TRIE::add(t[i]);
}
}
}
ans[x]+=TRIE::add(x);
}
int main(){
// freopen("in.txt","r",stdin);
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%lld",&n);
fo(i,1,n)scanf("%lld",&v[i]);
fo(i,1,n){
l[i]=len+1;
scanf("%s",str+len+1);
len=strlen(str+1);
}
l[n+1]=len+1;
fo(i,2,n){
scanf("%lld%lld",&X,&Y);
add(X,Y);add(Y,X);
}
dfs1(1);
dfs2(1);
fo(i,1,n)printf("%lld\n",ans[i]);
return 0;
}