【JZOJ 4503】 异或树 动态点分治

Description

这里写图片描述
这只熊孩子会搞 T 次破坏,点权为Ei
对于100%的数据 2<=n,T<=30000,Ei<=16384

Analysis

Ei<=16384=214 ,这启示了我们什么?
xor 怎么解决?可以拆位做,最后每个二进制位合并。
这题如果不搞破坏,点分治可做。
可是搞破坏,难道要打动态点分治?
没错!
首先把修改操作拆成位数个,对于该位0改成1,就加上0的点的权值减去1的点的权值。
但是怎么算任意点到该点的权值和?
把我们上面点分治的分治树copy下来,每次往父节点跳,对于当前点,算出它的兄弟点到它的权值和。当然最开始要算上它的子树到它的权值和。因为分治树的深度是 log2n 的,所以单次询问的复杂度是 O(log2n) 的。
一个小细节,分治树上的父子关系可能与原树上的不同,所以两点间距离要用原树上dis[u]+dis[v]-2*dis[lca]来解决。HowarLi貌似有一种高级的方法不用lca。
具体实现繁(wei)琐(suo),若有做此题的欲望应做好心理准备(本蒟蒻被虐了一周)。

Code

代码比较长。而且垫底了,so sad

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
#define efo(i,u) for(int i=last[u];i;i=next[i])
using namespace std;
typedef long long ll;
const int N=30010,M=N*2;
int n,m,tot,val,num,rt,root,to[M],next[M],wei[M],last[N];
int f[N][16],dep[N],dis[N],fa[N],size[N],sum[N][2];
ll ans,a[N];
struct node
{
    ll dis,s,a,id;
}b[N],c[N];
struct lyd
{
    ll s0,n0,fs;
}sm[N][15][2];
bool bz[N];
void link(int u,int v,int w)
{
    to[++tot]=v,wei[tot]=w,next[tot]=last[u],last[u]=tot;
}
void dfs1(int v,int from,int d,int k)
{
    f[v][0]=from,dep[v]=d,dis[v]=k;
    efo(i,v)
    {
        int u=to[i];
        if(u==from) continue;
        dfs1(u,v,d+1,k+wei[i]);
    }
}
int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    fd(i,int(log2(dep[u])),0)
        if(dep[f[u][i]]>=dep[v]) u=f[u][i];
    fd(i,int(log2(dep[u])),0)
        if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
    if(u!=v) return f[u][0];
    return u;
}
ll dist(int x,int y)
{
    return dis[x]+dis[y]-2*dis[lca(x,y)];
}
void getrt(int v,int from,int num)
{
    size[v]=1;
    int x=0;
    efo(i,v)
    {
        int u=to[i];
        if(u==from || bz[u]) continue;
        getrt(u,v,num);
        x=max(x,size[u]);
        size[v]+=size[u];
    }
    int mx=max(x,num-size[v]);
    if(mx<val) val=mx,rt=v;
}
void dfs(int v,int from,int S,int k)
{
    b[++m].s=S,b[m].dis=k,b[m].a=a[v],b[m].id=v;
    efo(i,v)
    {
        int u=to[i];
        if(u==from || bz[u]) continue;
        dfs(u,v,S,k+wei[i]);
    }
}
void divide(int v,int from,int num)
{
    val=2147483647;
    getrt(v,from,num);
    fa[rt]=from;
    if(!root) root=rt,fa[root]=root;
    int rt1=rt;
    m=0;
    efo(i,rt)
    {
        int u=to[i];
        if(u==from || bz[u]) continue;
        dfs(u,rt,u,wei[i]);
    }
    fo(i,1,m) ans+=(b[i].a^a[rt])*b[i].dis;
    fo(j,0,14)
    {
        int c1=0;
        ll s0=0,n0=0,t=0;
        sm[rt][j][(a[rt]&(1<<j))>=1].n0=1;
        sm[rt][j][(a[rt]&(1<<j))>=1].fs+=dist(rt,fa[rt]);
        fo(i,1,m)
        {
            bool p=(b[i].a&(1<<j))>=1;
            sm[rt][j][p].n0++,sm[rt][j][p].s0+=b[i].dis;
            sm[rt][j][p].fs+=dist(fa[rt],b[i].id);
            if(p) c[++c1]=b[i];
            else
            {
                s0+=b[i].dis,n0++;
                sum[b[i].s][0]+=b[i].dis,sum[b[i].s][1]++;
            }
        }
        fo(i,1,c1) t+=c[i].dis*(n0-sum[c[i].s][1])+s0-sum[c[i].s][0];
        fo(i,1,m) sum[b[i].s][0]=sum[b[i].s][1]=0;
        ans+=t*(1<<j);
    }
    bz[rt]=1;
    efo(i,rt1)
    {
        int u=to[i];
        if(u==from || bz[u]) continue;
        divide(u,rt1,size[u]);
    }
}
ll change(int v,int x,bool bz)
{
    ll t=sm[v][x][bz].s0-sm[v][x][1-bz].s0,s0,n0,len;
    for(int u=v;;u=fa[u])
    {
        if(u==root) break;
        len=dist(v,fa[u]);
        s0=sm[fa[u]][x][bz].s0-sm[u][x][bz].fs;
        n0=sm[fa[u]][x][bz].n0-sm[u][x][bz].n0;
        t+=s0+n0*len;
        s0=sm[fa[u]][x][1-bz].s0-sm[u][x][1-bz].fs;
        n0=sm[fa[u]][x][1-bz].n0-sm[u][x][1-bz].n0;
        t-=s0+n0*len;
    }
    for(int u=v;;u=fa[u])
    {
        sm[u][x][1-bz].n0++,sm[u][x][bz].n0--;
        if(u==root) break;
        len=dist(v,fa[u]);
        sm[u][x][1-bz].fs+=len,sm[u][x][bz].fs-=len;
        sm[fa[u]][x][1-bz].s0+=len,sm[fa[u]][x][bz].s0-=len;
    }
    return t;
}
int main()
{
    int _,u,v,w;
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,n-1)
    {
        scanf("%d %d %d",&u,&v,&w);
        link(u,v,w),link(v,u,w);
    }
    dfs1(1,1,1,0);
    fo(j,1,int(log2(n)))
        fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
    divide(1,0,n);
    scanf("%d",&_);
    while(_--)
    {
        scanf("%d %d",&v,&w);
        fo(i,0,14)
            if((a[v]^w)&(1<<i)) ans+=(1<<i)*change(v,i,a[v]&(1<<i));
        a[v]=w;
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值