hdu 5571 tree (动态点分治)

来源:互联网 发布:二级vb软件序列号 编辑:程序博客网 时间:2024/05/29 10:43

题目描述

传送门

题目大意:给出一棵n个节点的树,每个点有一个权值ai,一个点对(i,j)i<j的贡献为(ai xor aj)dis(i,j),求每次修改一个节点后树中所有点对的贡献和。

题解

带修改的路径问题很适合用动态点分治来做。
这道题如果我们直接考虑ai xor aj,不好做。所以我们考虑把每一位分离开,那么问题就转化成统计01对的个数以及路径和的问题。
对于每个点统计其作为重心时子树中每一位01的个数即路径和即可。

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#define M 15#define N 60003#define inf 1000000000#define LL long long using namespace std;int tot,n,m,nxt[N],point[N],v[N],mi[20],fa[N][20];int belong[N],f[N],size[N],vis[N],deep[N],val[N],sum,root; LL ans,len[N],dis[N];struct data{    LL sum[3][17],cnt[3][17];}tr[N],tr1[N];void add(int x,int y,int z){    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; len[tot]=z;    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; len[tot]=z;}void dfs(int x,int father){    deep[x]=deep[father]+1;    for (int i=1;i<=17;i++) {        if (deep[x]-mi[i]<0) break;        fa[x][i]=fa[fa[x][i-1]][i-1];    }    for (int i=point[x];i;i=nxt[i]) {        if (v[i]==father) continue;        fa[v[i]][0]=x;        dis[v[i]]=dis[x]+len[i];        dfs(v[i],x);    }}int lca(int x,int y){    if (deep[x]<deep[y]) swap(x,y);    int k=deep[x]-deep[y];    for (int i=0;i<=17;i++)     if ((k>>i)&1) x=fa[x][i];    if (x==y) return x;    for (int i=17;i>=0;i--)     if (fa[x][i]!=fa[y][i])      x=fa[x][i],y=fa[y][i];    return fa[x][0];}void getroot(int x,int father){    f[x]=0; size[x]=1;    for (int i=point[x];i;i=nxt[i]){        if (v[i]==father||vis[v[i]]) continue;        getroot(v[i],x);        size[x]+=size[v[i]];        f[x]=max(f[x],size[v[i]]);    }    f[x]=max(f[x],sum-size[x]);    if (f[x]<f[root]) root=x;}LL dist(int x,int y){    return dis[x]+dis[y]-2*dis[lca(x,y)];}void divi(int x,int father){    belong[x]=father; vis[x]=1;    for (int i=point[x];i;i=nxt[i]){        if (vis[v[i]]) continue;        root=0; sum=size[v[i]];        getroot(v[i],x);        divi(root,x);    }}void change(int u,int son,int v,int v1,LL tap){    int t=v1; LL D=dist(u,v);    if(u==v) {        for (int i=0;i<=M;i++) {            int opt=(t>>i)&1; opt^=1;            LL sum=tr[u].sum[opt][i];            LL cnt=tr[u].sum[opt][i];            if (cnt) ans+=tap*(sum+cnt*D)*(LL)mi[i];        }     }    else {        for (int i=0;i<=M;i++) {            int opt=(t>>i)&1; opt^=1;            LL sum=tr[u].sum[opt][i]-tr1[son].sum[opt][i];            LL cnt=tr[u].cnt[opt][i]-tr1[son].cnt[opt][i];            if (cnt) ans+=tap*(sum+cnt*D)*(LL)mi[i];        }    }    for (int i=0;i<=M;i++) {        tr[u].sum[(t>>i)&1][i]+=D*tap;         tr[u].cnt[(t>>i)&1][i]+=tap;     }    if (!belong[u]) return;    int f=belong[u]; D=dist(f,v);     t=v1;     for (int i=0;i<=M;i++) {     tr1[u].sum[(t>>i)&1][i]+=D*tap;     tr1[u].cnt[(t>>i)&1][i]+=tap;    }    change(belong[u],u,v,v1,tap);}int main(){    freopen("a.in","r",stdin);    freopen("my.out","w",stdout);    mi[0]=1;    for (int i=1;i<=17;i++) mi[i]=mi[i-1]*2;    while (scanf("%d",&n)!=EOF) {        tot=0; ans=0;        memset(point,0,sizeof(point));        memset(dis,0,sizeof(dis));        memset(vis,0,sizeof(vis));        memset(fa,0,sizeof(fa));        for (int i=1;i<=n;i++)         for (int j=0;j<=M;j++)           for (int k=0;k<=1;k++)           tr[i].sum[k][j]=tr[i].cnt[k][j]=tr1[i].sum[k][j]=tr1[i].cnt[k][j]=0;        for (int i=1;i<=n;i++) scanf("%d",&val[i]);        for (int i=1;i<n;i++) {            int x,y,z; scanf("%d%d%d",&x,&y,&z);            add(x,y,z);        }        dfs(1,0);        sum=n; root=0; f[0]=inf;         getroot(1,0);         divi(root,0);        for (int i=1;i<=n;i++)          change(i,i,i,val[i],1);        scanf("%d",&m);        for (int i=1;i<=m;i++) {            int x,v1; scanf("%d%d",&x,&v1);            change(x,x,x,val[x],-1);            change(x,x,x,v1,1); val[x]=v1;            printf("%lld\n",ans);        }    }}
0 0