2016.4.06Test:problem1:异或树:动态树的点分治

来源:互联网 发布:手机 usb 共享网络 编辑:程序博客网 时间:2024/06/03 14:48

题目大意:给一棵树有点权有边权,每次操作可以修改一个点的点权,求所有的(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 longusing 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);}}






2 0
原创粉丝点击