题目描述
一棵树,
n
个节点,有点权
有
T
次操作,每次操作会修改一个点的点权。
询问每次操作之后下列式子的值
2≤n,T≤30000,0≤ci≤100,0≤ai<214
题目分析
既然有异或,那么我们就将点权拆位,然后统计答案。
点剖,记录每个分治块中点权为
0
/
具体过程与[ZJOI2015]幻想乡战略游戏类似,可以参考我的博客。
时间复杂度
O(14(n+T)log2n)
。
注意将枚举位数循环放在最里面,减少不必要的计算,就可以从4000ms+变成1000ms-。
代码实现
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
typedef long long LL;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch))
{
if (ch=='-') f=-1;
ch=getchar();
}
while (isdigit(ch))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int buf[30];
void write(LL x)
{
if (x<0) putchar('-'),x=-x,buf[0]=0;
while (x)
{
buf[++buf[0]]=x%10;
x/=10;
}
while (buf[0]) putchar('0'+buf[buf[0]--]);
}
const int N=30500;
const int M=N<<1;
const int EL=N<<1;
const int LGEL=16;
const int D=15;
int h[N],last[N],fa[N],size[N],prt[N],a[N],pos[N],d[N],cnt[N][D][2];
int tov[M],next[M],len[M];
LL sum[N][D][2],g[N][D][2];
int rmq[EL][LGEL];
int n,t,tot,el,rt;
int euler[EL];
bool vis[N];
int two[D];
LL ans;
void insert(int x,int y,int z)
{
tov[++tot]=y,len[tot]=z,next[tot]=last[x],last[x]=tot;
}
void dfs(int x)
{
euler[++el]=x,pos[x]=el;
for (int i=last[x],y;i;i=next[i])
if (fa[x]!=(y=tov[i]))
h[y]=h[fa[y]=x]+len[i],d[y]=d[x]+1,dfs(y),euler[++el]=x;
}
void pre()
{
int lgel=trunc(log(el)/log(2));
for (int i=1;i<=el;i++) rmq[i][0]=euler[i];
for (int j=1;j<=lgel;j++)
for (int i=1;i+(1<<j)-1<=el;i++)
if (d[rmq[i][j-1]]<d[rmq[i+(1<<j-1)][j-1]])
rmq[i][j]=rmq[i][j-1];
else
rmq[i][j]=rmq[i+(1<<j-1)][j-1];
}
int rmq_get(int l,int r)
{
int lgr=trunc(log(r-l+1)/log(2));
if (d[rmq[l][lgr]]<d[rmq[r-(1<<lgr)+1][lgr]])
return rmq[l][lgr];
else
return rmq[r-(1<<lgr)+1][lgr];
}
int lca(int x,int y)
{
x=pos[x],y=pos[y];
if (x>y) x^=y^=x^=y;
return rmq_get(x,y);
}
int dist(int x,int y)
{
int z=lca(x,y);
return h[x]+h[y]-(h[z]<<1);
}
int head,tail,que[N],top[N];
int core(int c)
{
que[1]=c,fa[c]=0,head=0,tail=1;
while (head!=tail)
{
int x=que[++head];
size[x]=1;
for (int i=last[x],y;i;i=next[i])
if (!vis[y=tov[i]]&&y!=fa[x])
fa[que[++tail]=y]=x;
}
for (head=tail;head;head--)
size[fa[que[head]]]+=size[que[head]];
int ret=0,mi=n;
for (head=1;head<=tail;head++)
{
int x=que[head],tmp=size[c]-size[x];
for (int i=last[x],y;i;i=next[i])
if (!vis[y=tov[i]]&&y!=fa[x]&&tmp<size[y])
tmp=size[y];
if (mi>tmp) ret=x,mi=tmp;
}
return ret;
}
int build(int c,int pt)
{
vis[c=core(c)]=true;
LL sum0=0,g0=pt?dist(c,pt):0;
int s0=1;
for (int i0=last[c],x;i0;i0=next[i0])
if (!vis[x=tov[i0]])
{
top[x]=len[i0],fa[x]=c;
que[1]=x=tov[i0],head=0,tail=1;
while (head!=tail)
{
sum0+=top[x=que[++head]],g0+=pt?dist(x,pt):0,s0++;
for (int i=last[x],y;i;i=next[i])
if ((y=tov[i])!=fa[x]&&!vis[y])
que[++tail]=y,fa[y]=x,top[y]=top[x]+len[i];
}
}
for (int i=0;i<D;i++)
g[c][i][0]=g0,sum[c][i][0]=sum0,cnt[c][i][0]=s0;
for (int i=last[c],y;i;i=next[i])
if (!vis[y=tov[i]]) prt[build(y,c)]=c;
return c;
}
int ods[D],nws[D];
void modify(int x)
{
int og=x;
for (int p=0,d,d1;x;p=x,x=prt[x])
{
d=dist(og,x);
for (int di=0;di<D;di++)
ans+=(sum[x][di][nws[di]^1]+1ll*cnt[x][di][nws[di]^1]*d-sum[x][di][ods[di]^1]-1ll*cnt[x][di][ods[di]^1]*d)*two[di];
if (prt[x])
{
d1=dist(og,prt[x]);
for (int di=0;di<D;di++)
ans+=(g[x][di][ods[di]^1]+1ll*cnt[x][di][ods[di]^1]*d1-g[x][di][nws[di]^1]-1ll*cnt[x][di][nws[di]^1]*d1)*two[di];
}
for (int di=0;di<D;di++)
{
cnt[x][di][ods[di]]--,cnt[x][di][nws[di]]++;
sum[x][di][ods[di]]-=d,sum[x][di][nws[di]]+=d;
if (p) g[p][di][ods[di]]-=d,g[p][di][nws[di]]+=d;
}
}
}
void change(int x,int od,int nw)
{
for (int i=0;i<D;i++,od>>=1,nw>>=1)
ods[i]=od&1,nws[i]=nw&1;
modify(x);
}
int main()
{
ans=0,two[0]=1;
for (int i=1;i<D;i++) two[i]=two[i-1]<<1;
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1,x,y,z;i<n;i++)
{
x=read(),y=read(),z=read();
insert(x,y,z),insert(y,x,z);
}
dfs(1);
pre();
rt=build(1,0);
for (int i=1;i<=n;i++) change(i,0,a[i]);
for (t=read();t;t--)
{
int d=read(),e=read();
change(d,a[d],e);
a[d]=e;
write(ans),putchar('\n');
}
fclose(stdin);
fclose(stdout);
return 0;
}