题目大意:给一棵树有点权有边权,每次操作可以修改一个点的点权,求所有的(a[x]^a[y])*dis(x,y)的和满足(x<y),点数<=30000,操作数<=30000,点权<=16384
20分暴力滚粗QAQ
做法:树分治。那个点权<=16384显然是2^14,这就提示我们用二进制分解
对于每次更新操作,可以认为是位数次更新,对于每个从1变成0的操作。我们减去树中那一位为0的到该点的路径之和,加上树中那一位为1的到该点的路径之和,从0变成1类似。
问题转换成每次查询所有值为x的点(x∈[0,1])到某个点的距离之和是多少并更新,这是可以通过点分治来做的。
在点分治中,对于每一层的树,我们记录其中一些点的信息(曾经被选作根的点)。需要记录的信息有以该点为根的子树中,0和1的数量,0和1到该点的距离之和。
当我们查询与修改时,只要在每层点分治树上做就可以了,为了避免重复计算我们记录根(g1)以及根的儿子(g2)的信息。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=30010;
int n,tot=1,N,h[maxn],dep[maxn],fa[maxn];
struct edge{int to,next,w;}G[maxn*10];
LL rec[maxn*2][15][2],len[maxn],l[maxn][15];
int g1[maxn][15],g2[maxn][15],s[maxn*2][15][2];
int size[maxn],dp[maxn],a[maxn],q[maxn];
bool flag[maxn];
void add(int M,int x){
for (int i=0;i<=14;++i){
rec[M][i][(a[x]>>i)&1]+=len[x];
s[M][i][(a[x]>>i)&1]++;
}
}
void dfs(int x,int D,int M){
add(M,x); g2[x][D]=M;
for (int i=h[x];i;i=G[i].next)
if (!flag[G[i].to]&&fa[G[i].to]==x)
dfs(G[i].to,D,M);
}
void bfs(int x,int D){
int t=0,head=1; ++N;
fa[x]=0; len[x]=0; q[1]=x;
while (t<head){
int now=q[++t];
l[now][D]=len[now]; dep[now]=D;
for (int i=h[now];i;i=G[i].next)
if (!flag[G[i].to]&&G[i].to!=fa[now]){
q[++head]=G[i].to; fa[G[i].to]=now;
len[G[i].to]=len[now]+G[i].w;
}
}
for (int i=1;i<=head;++i){
g1[q[i]][D]=N;
add(N,q[i]);
}
for (int i=h[x];i;i=G[i].next)
if (!flag[G[i].to])
dfs(G[i].to,D,++N);
}
int getroot(int u){
int t=0,head=1; q[1]=u; fa[u]=0;
while (t<head){
int now=q[++t]; size[now]=1; dp[now]=0;
for (int i=h[now];i;i=G[i].next)
if (!flag[G[i].to]&&G[i].to!=fa[now]){
q[++head]=G[i].to;
fa[G[i].to]=now;
}
}
for (int i=head;i>=2;--i){
size[fa[q[i]]]+=size[q[i]];
dp[fa[q[i]]]=max(dp[fa[q[i]]],size[q[i]]);
}
for (int i=2;i<=head;++i)
dp[q[i]]=max(dp[q[i]],size[q[1]]-size[q[i]]);
int ret=q[1];
for (int i=2;i<=head;++i)
if (dp[q[i]]<dp[ret]) ret=q[i];
return ret;
}
void build(int x,int D){
int root=getroot(x); bfs(root,D); flag[root]=1;
for (int i=h[root];i;i=G[i].next)
if (!flag[G[i].to]) build(G[i].to,D+1);
}
void add_edge(int x,int y,int z){
G[++tot].to=y;G[tot].next=h[x];h[x]=tot;G[tot].w=z;
}
LL ask(int x){
LL ret=0;
for (int i=0;i<=dep[x];++i)
for (int j=0;j<=14;++j){
int t=(a[x]>>j)&1;
ret+=rec[g1[x][i]][j][t^1]<<j;
ret+=(1LL*s[g1[x][i]][j][t^1]*l[x][i])<<j;
if (i<dep[x]){
ret-=rec[g2[x][i]][j][t^1]<<j;
ret-=(1LL*s[g2[x][i]][j][t^1]*l[x][i])<<j;
}
}return ret;
}
void change(int u,int x){
for(int i=0;i<=14;++i)
if(((a[u]>>i)&1)!=((x>>i)&1))
for (int j=0;j<=dep[u];++j){
int t=(a[u]>>i)&1,t1=g1[u][j],t2=g2[u][j];
rec[t1][i][t]-=l[u][j];
s[t1][i][t]--;
rec[t1][i][t^1]+=l[u][j];
s[t1][i][t^1]++;
if (g2[u][j]){
rec[t2][i][t]-=l[u][j];
s[t2][i][t]--;
rec[t2][i][t^1]+=l[u][j];
s[t2][i][t^1]++;
}
}
a[u]=x;
}
int main(){
scanf("%d",&n); N=1;
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
for (int i=1;i<n;++i){
int x,y,z; scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z); add_edge(y,x,z);
}
build(1,0);
int T; scanf("%d",&T); LL ans=0;
for (int i=1;i<=n;++i) ans+=ask(i);
ans/=2;
for (int i=1;i<=T;++i){
int x,y; scanf("%d%d",&x,&y);
ans-=ask(x); change(x,y); ans+=ask(x);
printf("%lld\n",ans);
}
}