1. 程式人生 > 其它 >點分樹

點分樹

點分樹,也叫動態點分治,就是對於一個樹性結構,按照重心的父子關係轉化成一顆深度嚴格為logn的樹

與普通點分治不同的是,他是動態的。。。就是可以在樹上的節點進行權值更改,然後暴力得到答案

這個暴力可不是從根節點一直往下搜,而是利用某些資料結構,或者一個比較簡單的遞推式,又或是啥的,來簡化暴力的過程

(優美的暴力)

所以我們對於重心的合理利用成了重點,當然,這也是點分治,容斥原理的利用還是非常重要的

震波

這個就是點分樹最最最最基礎的模板題了,就是要邊地震邊修改權值

然後加上稍微簡單一點的查詢

首先我們一定是要找到lca的,不然咋求兩點之間的距離??

(個人習慣用樹鏈剖分中的輕重鏈top來求)

int
siz[N],dep[N],son[N],fa[N]; void dfs1(int x,int f){ siz[x]=1; dep[x]=dep[f]+1; fa[x]=f; for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==f)continue; dfs1(y,x); siz[x]+=siz[y]; if(!son[x]||siz[y]>siz[son[x]])son[x]=y; } } int top[N]; void
dfs2(int x,int f){ top[x]=f; if(son[x])dfs2(son[x],f); for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==son[x]||y==fa[x])continue; dfs2(y,y); } } int get_lca(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); x=fa[top[x]]; }
return dep[x]<dep[y]?x:y; }

這樣我們就有一個模板可以利用來求兩點之間的連線

所以get_dis

int get_dis(int x,int y){
    return dep[x]+dep[y]-2*dep[get_lca(x,y)];
}

接下來就是關於我們去找重心然後更新權值的過程了

找重心並且將以重心為樹的連邊的重心樹建立,就是newfa[];

(重點是,每次的siz都要重新找,因為你的樹已經重構了,原來的siz已經不適用了)

int rt,alsiz,ms[N],mx;
bool vis[N];
void get_rt(int x,int f){
    siz[x]=1;ms[x]=0;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f||vis[y])continue;
        get_rt(y,x);
        siz[x]+=siz[y];
        ms[x]=max(ms[x],siz[y]);
    }
    ms[x]=max(ms[x],alsiz-siz[x]);
    if(ms[x]<mx)mx=ms[x],rt=x;
}
int newfa[N];
void get_siz(int x,int f){
    siz[x]=1;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f||vis[y])continue;
        get_siz(y,x);
        siz[x]+=siz[y];
    }
}
void pre_dfs(int x){
    vis[x]=1;get_siz(x,0);
    int tmp=siz[x]+5;
    t1[x].init(tmp);t2[x].init(tmp);
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(vis[y])continue;
        alsiz=mx=siz[y];get_rt(y,x);
        newfa[rt]=x;pre_dfs(rt);
    }
}

接下來就是計算過程,我們要根據題意來計算,每一次更新之後的答案

這裡運用樹狀陣列來維護,當然線段樹也可以,不過我覺得我的碼量已經夠大了,就別禍害我的編譯器了

(這裡資料範圍過大,陣列開不出,用vector,但是記住,在用他的時候,一定要先插入零)

tr[i].resize(x);

或者是我在下面程式碼寫的那種,上面的是我在看題解的時候看到的,好像比我直接插入快好多~~~!!!

struct bit_tree{
    vector<int> tr;
    int trsiz;
    void init(int x){
        trsiz=x+1;for(re i=0;i<=trsiz;i++)tr.push_back(0);
    }
    void add(int x,int v){
        x=min(x+1,trsiz);
        for(re i=x;i<=trsiz;i+=(i&(-i)))tr[i]+=v;//cout<<i<<" ";
        //cout<<endl;
    } 
    int query(int x){
        x=min(x+1,trsiz);int ret=0;
        for(re i=x;i;i-=(i&(-i)))ret+=tr[i];
        return ret;
    }
}t1[N],t2[N];

然後就剩下最後的更新和查找了

這個題要更新的是每個點的權值,而且是覆蓋式更新,所以我們要先做差,再更新

然後,為什麼可以用樹狀陣列呢?

每一次我們都以節點之間的距離為下標,更新這個點的樹狀陣列;

查詢的時候,直接根據限制的距離,在每一個樹狀陣列中查詢

記得容斥原理,定義兩個樹狀陣列,然後加爹的,減兒子的(這裡指的是重心樹中的父子關係)

void get_up(int x,int v){
    for(re i=x;i;i=newfa[i])t1[i].add(get_dis(i,x),v);
    //cout<<"sb"<<endl;
    for(re i=x;newfa[i];i=newfa[i])t2[i].add(get_dis(newfa[i],x),v);
}
int get_ans(int x,int len){
    int ret=t1[x].query(len);
    for(re i=x;newfa[i];i=newfa[i]){
        int tmp=get_dis(newfa[i],x);
        if(len>=tmp)ret+=t1[newfa[i]].query(len-tmp)-t2[i].query(len-tmp);
    }
    return ret;
}

完整程式碼

#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=100005;
int n,m;
int val[N];
struct bit_tree{
    vector<int> tr;
    int trsiz;
    void init(int x){
        trsiz=x+1;for(re i=0;i<=trsiz;i++)tr.push_back(0);
    }
    void add(int x,int v){
        x=min(x+1,trsiz);
        for(re i=x;i<=trsiz;i+=(i&(-i)))tr[i]+=v;//cout<<i<<" ";
        //cout<<endl;
    } 
    int query(int x){
        x=min(x+1,trsiz);int ret=0;
        for(re i=x;i;i-=(i&(-i)))ret+=tr[i];
        return ret;
    }
}t1[N],t2[N];
int to[N<<1],nxt[N<<1],head[N<<1],rp;
void add_edg(int x,int y){
    to[++rp]=y;
    nxt[rp]=head[x];
    head[x]=rp;
}
int siz[N],dep[N],son[N],fa[N];
void dfs1(int x,int f){
    siz[x]=1;
    dep[x]=dep[f]+1;
    fa[x]=f;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f)continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
    }
}
int top[N];
void dfs2(int x,int f){
    top[x]=f;
    if(son[x])dfs2(son[x],f);
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==son[x]||y==fa[x])continue;
        dfs2(y,y);
    }
}
int get_lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
int get_dis(int x,int y){
    return dep[x]+dep[y]-2*dep[get_lca(x,y)];
}
int rt,alsiz,ms[N],mx;
bool vis[N];
void get_rt(int x,int f){
    siz[x]=1;ms[x]=0;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f||vis[y])continue;
        get_rt(y,x);
        siz[x]+=siz[y];
        ms[x]=max(ms[x],siz[y]);
    }
    ms[x]=max(ms[x],alsiz-siz[x]);
    if(ms[x]<mx)mx=ms[x],rt=x;
}
int newfa[N];
void get_siz(int x,int f){
    siz[x]=1;
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==f||vis[y])continue;
        get_siz(y,x);
        siz[x]+=siz[y];
    }
}
void pre_dfs(int x){
    vis[x]=1;get_siz(x,0);
    int tmp=siz[x]+5;
    t1[x].init(tmp);t2[x].init(tmp);
    for(re i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(vis[y])continue;
        alsiz=mx=siz[y];get_rt(y,x);
        newfa[rt]=x;pre_dfs(rt);
    }
}
void get_up(int x,int v){
    for(re i=x;i;i=newfa[i])t1[i].add(get_dis(i,x),v);
    for(re i=x;newfa[i];i=newfa[i])t2[i].add(get_dis(newfa[i],x),v);
}
int get_ans(int x,int len){
    int ret=t1[x].query(len);
    for(re i=x;newfa[i];i=newfa[i]){
        int tmp=get_dis(newfa[i],x);
        if(len>=tmp)ret+=t1[newfa[i]].query(len-tmp)-t2[i].query(len-tmp);
    }
    return ret;
}
signed main(){
    scanf("%d%d",&n,&m);    
    for(re i=1;i<=n;i++)scanf("%d",&val[i]);
    for(re i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        add_edg(x,y);add_edg(y,x);
    }
    dfs1(1,0);
    dfs2(1,1);
    alsiz=mx=n;
    get_rt(1,0);
    pre_dfs(rt);
    for(re i=1;i<=n;i++)get_up(i,val[i]);
    int ans=0;
    for(re i=1;i<=m;i++){
        int typ,x,y;scanf("%d%d%d",&typ,&x,&y);
        x^=ans;y^=ans;
        if(typ)get_up(x,y-val[x]),val[x]=y;
        else printf("%d\n",ans=get_ans(x,y));
    }
}
View Code