HDU 6793 Tokitsukaze and Colorful Tree(离线+树状数组)

对于这一类的很多操作,每次操作计算答案的题目,大部分都是前后答案有关的,也就是改变了当前操作的影响。

对于本题来说,一个朴素的想法是,我们对于当前的改变答案,影响到的点不包括子树中的,以及点到根路径上的点

这样可以简单的想到,看上去非常像树状数组,这样可以快速求。但是这里有一个问题,因为我们每次修改的答案都不一样

所以我们对于每种颜色都要维护一个数据结构,这样肯定炸内存了。

进一步去发现性质,我们发现对于答案的贡献,每个颜色都是独立的,因此想到能否对颜色进行离线再求取答案,这样我们每次做完一个颜色就能清空数据结构求下一个

只需要维护一个就行了。对颜色离线完后,因为是异或,所以显然能够想到每一位独立维护查询贡献,这是老套路,因为异或的每位互不影响。

在细节上,首先维护子树很简单,只需要dfs序建立树状数组单点修改即可。但是到根的路径如果树链剖分,复杂度不太行,因此考虑用另一个树状数组维护

利用差分的形式,每次修改子树,这样我们计算答案的时候只需要查询一次就能够算出所有根上的点对他影响。

总体来说这题用到的知识都比较经典,这启发我们要把基础打牢

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> pll;
const int N=1e5+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int dfn[N],col[N],val[N];
int tr[N],idx;
int sz[N],times;
int h[N],ne[N<<1],e[N<<1];
int now[2];
int n;
ll ans[N];
struct node{
    int id,w,v,k;
};
struct bit{
    int tr[N];
    void init() {memset(tr,0,sizeof tr);}
    int lowbit(int x){
        return x&-x;
    }
    void add(int x,int c){
        int i;
        for(i=x;i<=n;i+=lowbit(i)){
            tr[i]+=c;
        }
    }
    int sum(int x){
        int i;
        int ans=0;
        for(i=x;i;i-=lowbit(i)){
            ans+=tr[i];
        }
        return ans;
    }
}A[2],B[2];
vector<node> num[N];
void init(){
    for(int i=0;i<=n;i++){
        h[i]=-1;
        sz[i]=0;
        ans[i]=0;
    }
    times=idx=0;
    for(int i=1;i<=n;i++)
        num[i].clear();
}
void dfs(int u,int fa){
    dfn[u]=++times;
    int i;
    sz[u]=1;
    for(i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(j==fa)
            continue;
        dfs(j,u);
        sz[u]+=sz[j];
    }
}
void work(int u){
    int i,j;
    //对于每位进行计算贡献,因为他们都是独立的
    for(j=0;j<20;j++){
        for(auto x:num[u]){//当前颜色中的所有操作,因为都是按序来的,所以贡献正确
            A[(x.v>>j)&1].add(dfn[x.w],x.k);//这是维护子树影响的,因为询问时到子树的答案都不能算
            B[(x.v>>j)&1].add(dfn[x.w],x.k);
            B[(x.v>>j)&1].add(dfn[x.w]+sz[x.w],-x.k);//对于B,进行区间修改,这样查询的时候就能知道父亲对自己的影响,也就是到根的路径
            now[0]=A[0].sum(n)+A[0].sum(dfn[x.w])-A[0].sum(dfn[x.w]+sz[x.w]-1);//全部的个数减去子树个数
            now[1]=A[1].sum(n)+A[1].sum(dfn[x.w])-A[1].sum(dfn[x.w]+sz[x.w]-1);//同理
            now[0]-=B[0].sum(dfn[x.w]);//减去到根的影响
            now[1]-=B[1].sum(dfn[x.w]);
            ans[x.id]+=1ll*x.k*(1<<j)*now[((x.v>>j)&1)^1];
        }
    }
}
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void solve(){
    cin>>n;
    int i;
    init();
    for(i=1;i<=n;i++){
        cin>>col[i];
    }
    for(i=1;i<=n;i++){
        cin>>val[i];
    }
    for(i=1;i<=n;i++)
        num[col[i]].push_back({0,i,val[i],1}); //存放对应颜色的操作,序号都为第一个
    for(i=1;i<n;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
        add(b,a);
    }
    dfs(1,-1);
    int q;
    cin>>q;
    for(i=1;i<=q;i++){
        int opt,x,c;
        cin>>opt>>x>>c;
        num[col[x]].push_back({i,x,val[x],-1});//先删除当前的状态
        if(opt==1){
            val[x]=c;
            num[col[x]].push_back({i,x,val[x],1});
        }
        else{
            col[x]=c;
            num[col[x]].push_back({i,x,val[x],1});
        }
        //加入新状态
    }
    for(i=1;i<=n;i++){
        num[col[i]].push_back({q+1,i,val[i],-1});
    }
    for(i=1;i<=n;i++){
        work(i);
    }
    for(i=1;i<=q;i++){
        ans[i]+=ans[i-1];
    }
    for(i=0;i<=q;i++)
        cout<<ans[i]<<endl;
}
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    A[0].init();
    A[1].init();
    B[0].init();
    B[1].init();
    while(t--){
        solve();
    }
}
View Code

 

posted @ 2020-08-29 21:43  朝暮不思  阅读(271)  评论(0编辑  收藏  举报