【Bzoj3626】LCA

題意

給出一個n個節點的有根樹(編號爲0到n-1,根節點爲0)。一個點的深度定義爲這個節點到根的距離+1。
設dep[i]表示點i的深度,LCA(i,j)表示i與j的最近公共祖先。
有q次詢問,每次詢問給出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]區間內的每個節點i與z的最近公共祖先的深度之和)


解析

  首先發現一個性質,加上depth[LCA(l,r)]可以等價於將根到l加上1,之後查詢根到r。擴展到區間的話,設要求結點爲z,區間[l,r]。也就是從1加到r後對z的查詢值減去1加到l-1對z的查詢值。這樣的話可以把詢問先離線排序,之後樹鏈剖分維護,在標記一下就可以了。


#include <cstdio>
#include <algorithm>

#define Rep( i , B , E ) for(int i=B;i<=E;i++)
#define For( i , B , E ) for(int i=B;i;i=E)

using namespace std;

const int maxx = 100000 + 25;
const int mod = 201314;

int head[maxx],to[maxx<<1],nxt[maxx<<1];
int top[maxx],ftr[maxx],son[maxx],rnk[maxx],size[maxx],dpt[maxx];
int T[maxx<<2],Add[maxx<<2],Ans[maxx];

int n,m,x,y,z,num,cnt,tot,pos = 1;

namespace Y{

    void Ins(int x,int y) {to[++num]=y;nxt[num]=head[x];head[x]=num;}

    void Dfs1(int x){
        size[x] = 1;
        For( i , head[x] , nxt[i] ){
            int now = to[i];if(now == ftr[x]) continue;
            dpt[now] = dpt[x] + 1;ftr[now] = x;
            Dfs1(now);size[x] += size[now];
            if(size[now] > size[son[x]]) son[x] = now;
        }
    }

    void Dfs2(int x,int brn){
        rnk[x] = ++cnt;top[x] = brn;
        if(son[x]) Dfs2(son[x],brn);
        For( i , head[x] , nxt[i] )
            if(to[i] != ftr[x] && to[i] != son[x])
                Dfs2(to[i],to[i]);
    }

    void pushdown(int i,int l,int r){
        int mid = (l+r) >> 1;int k = Add[i];
        T[i<<1] += (mid-l+1)*k;T[i<<1|1] += (r-mid)*k;
        Add[i<<1] += k;Add[i<<1|1] += k;Add[i] = 0;
    }

    void modify(int i,int x,int y,int l,int r,int k){
        if(x <= l && r <= y) {T[i] += (r-l+1)*k;Add[i] += k;return;}
        if(Add[i]) pushdown(i,l,r);int mid = (l+r) >> 1;
        if(x <= mid) modify(i<<1,x,y,l,mid,k);
        if(y >  mid) modify(i<<1|1,x,y,mid+1,r,k);
        T[i] = (T[i<<1] + T[i<<1|1]) % mod;
    }

    int Query(int i,int x,int y,int l,int r){
        if(x <= l && r <= y) return T[i] % mod;int ans = 0;
        int mid = (l+r) >> 1;if(Add[i]) pushdown(i,l,r);
        if(x <= mid) ans = (ans + Query(i<<1,x,y,l,mid)) % mod;
        if(y >  mid) ans = (ans + Query(i<<1|1,x,y,mid+1,r)) % mod;
        return ans;
    }

    void update(int x,int y,int k){
        while(top[x] != top[y]){
            if(dpt[top[x]] > dpt[top[y]]) x^=y^=x^=y;
            modify(1,rnk[top[y]],rnk[y],1,n,k);
            y = ftr[top[y]];
        }
        if(rnk[x] > rnk[y]) x^=y^=x^=y;
        modify(1,rnk[x],rnk[y],1,n,k);
    }

    int Get(int x,int y){
        int ans = 0;
        while(top[x] != top[y]){
            if(dpt[top[x]] > dpt[top[y]]) x^=y^=x^=y;
            ans = (ans + Query(1,rnk[top[y]],rnk[y],1,n)) % mod;
            y = ftr[top[y]];
        }
        if(rnk[x] > rnk[y]) x^=y^=x^=y;
        ans = (ans + Query(1,rnk[x],rnk[y],1,n)) % mod;
        return ans;
    }

}

namespace Question{

    struct Que{
        int l;
        int f;
        int d;
        int id;
    }Q[maxx];

    bool cmp(Que a,Que b){
        if(a.l != b.l) return a.l < b.l;
        return a.id < b.id; 
    }

}

using namespace Y;
using namespace Question;

int main(){
    scanf("%d%d",&n,&m);
    Rep( i , 2 , n ) scanf("%d",&x),Ins(i,x+1),Ins(x+1,i);
    Dfs1(1);Dfs2(1,1);
    Rep( i , 1 , m ){
        scanf("%d%d%d",&x,&y,&z);x++;y++;z++;
        Q[++tot].id = i;Q[tot].l = x-1;Q[tot].d = z;Q[tot].f = 1;
        Q[++tot].id = i;Q[tot].l = y;  Q[tot].d = z;Q[tot].f = -1;
    }
    sort(Q+1,Q+tot+1,cmp);
    Rep( i , 1 , tot ){
        while(pos <= Q[i].l){
            update(1,pos,1);
            pos ++;
        }
        if(Q[i].f == 1) Ans[Q[i].id] -= Get(1,Q[i].d);
        if(Q[i].f == -1) Ans[Q[i].id] += Get(1,Q[i].d);
    }
    Rep( i , 1 , m ) printf("%d\n",(Ans[i]%mod+mod)%mod);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章