[JZOJ4503]异或树

题目描述

一棵树, n 个节点,有点权ai和边权 ci
T 次操作,每次操作会修改一个点的点权。
询问每次操作之后下列式子的值

x<y(ax xor ay)×dist(x,y)

2n,T30000,0ci100,0ai<214


题目分析

既然有异或,那么我们就将点权拆位,然后统计答案。
点剖,记录每个分治块中点权为 0 /1(拆位后)的点到分治中心的距离和以及个数,然后统计答案。修改时就直接从修改点开始往分治父亲点跳,不断修改。
具体过程与[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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值