[动态点分治] BZOJ3730: 震波

题意

给定N个点的一棵边权都为1的树,每个点有点权 。M次操作,两种类型
1.单点点权修改。
2.给出x和k,询问到x的距离不超过K的所有点权和。
操作加密,强制在线。
N,M<=100000

题解

动态点分治。
同样把信息收集到根。对于每个点分树,把所有点放到一个树状数组中,数状数组的下标即是到根的距离。要注意相同子树中的东西计算重复了,为了抠去重复部分,还需要对每个点分树再开一个数状数组,维护其中的点到父点分树的根的距离。这样修改和询问都是暴力爬树高,修改时在途中进行树状数组的单点修改,询问的话每次统计一下经过当前根的路径对应的权值和即可。

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=100005, maxe=200005;
int n,m,w[maxn],last_print;
int fir[maxn],nxt[maxe],son[maxe],tot;
bool vis[maxn];
int sz[maxn];
//------------------------------------------------------------------------
int rt,allmin,allsz,MaxD,d[maxn];
int dfs_info(int x,int fa){
    sz[x]=1;
    for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa&&!vis[son[j]]){
        d[son[j]]=d[x]+1; MaxD=max(MaxD,d[son[j]]);
        dfs_info(son[j],x); sz[x]+=sz[son[j]];
    }
}
void dfs_G(int x,int fa){
     int _max=0;  
     for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa&&!vis[son[j]]){
        dfs_G(son[j],x);  
        _max=max(_max,sz[son[j]]);  
     }  
     if(allmin>max(_max,allsz-sz[x])) allmin=max(_max,allsz-sz[x]), rt=x;  
}
int find_G(int x){
    allmin=1e+9; 
    MaxD=d[x]=0; dfs_info(x,0); allsz=sz[x];
    dfs_G(x,0);
    return rt;
}
//------------------------------------------------------------------------
int dep[maxn],anc[maxn][25];
void dfs_LCA(int x,int fa){
    anc[x][0]=fa;
    for(int j=1;j<=20;j++) anc[x][j]=anc[anc[x][j-1]][j-1];
    for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa)
    dep[son[j]]=dep[x]+1, dfs_LCA(son[j],x);
}
int LCA(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int j=20;j>=0;j--) if(dep[anc[x][j]]>=dep[y]) x=anc[x][j]; 
    if(x==y) return x;
    for(int j=20;j>=0;j--) if(anc[x][j]!=anc[y][j]) x=anc[x][j], y=anc[y][j];
    return anc[x][0];
}
int getDis(int x,int y){
    return dep[x]+dep[y]-dep[LCA(x,y)]*2;
}
//------------------------------------------------------------------------
vector< int > bit[maxn][2];
void bit_Updata(int k1,int k2,int x,int val){
    int len=bit[k1][k2].size()-1;
    for(x++;x<=len;x+=(x&(-x))) bit[k1][k2][x]+=val;
}
int bit_Query(int k1,int k2,int x){
    int res=0, len=bit[k1][k2].size()-1;
    for(x=min(x+1,len);x;x-=(x&(-x))) res+=bit[k1][k2][x];
    return res;
}
//------------------------------------------------------------------------
int prt[maxn];
void dfs_push(int x,int fa,int id){
    bit_Updata(id,0,getDis(x,id),w[x]); 
    if(prt[id]) bit_Updata(id,1,getDis(x,prt[id]),w[x]);    
    for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa&&!vis[son[j]]) dfs_push(son[j],x,id);
}
void DivTree(int x,int faG){
    int G=find_G(x); 
    prt[G]=faG; vis[G]=true;
    bit[G][0].resize(allsz+3); bit[G][1].resize(allsz+3); 
    dfs_push(G,0,G);
    for(int j=fir[G];j;j=nxt[j]) if(!vis[son[j]]) DivTree(son[j],G);
}
void Updata(int x,int val){
    for(int y=x;y;y=prt[y]){
        bit_Updata(y,0,getDis(x,y),val-w[x]);
        if(prt[y]) bit_Updata(y,1,getDis(x,prt[y]),val-w[x]);       
    }
}
int Query(int x,int K){
    int res=bit_Query(x,0,K);
    for(int y=x;prt[y];y=prt[y]) if(K>=getDis(x,prt[y]))
     res+=bit_Query(prt[y],0,K-getDis(x,prt[y]))-bit_Query(y,1,K-getDis(x,prt[y]));
    return res;
}
//------------------------------------------------------------------------
void add(int x,int y){
    son[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot;
}
int getint(){
    int res=0; char ch=getchar();
    while(!('0'<=ch&&ch<='9')) ch=getchar();
    while('0'<=ch&&ch<='9') res=res*10+ch-'0', ch=getchar();
    return res;
}
int main(){
    freopen("bzoj3730.in","r",stdin);
    freopen("bzoj3730.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) w[i]=getint();
    for(int i=1;i<=n-1;i++){
        int x=getint(),y=getint(); 
        add(x,y); add(y,x);
    }
    dfs_LCA(1,0);
    DivTree(1,0);
    while(m--){
        int pd=getint(),x=getint(),y=getint();
        x^=last_print; y^=last_print;
        if(!pd) printf("%d\n",last_print=Query(x,y));
           else Updata(x,y), w[x]=y;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值