1. 程式人生 > >樹剖+線段樹--bzoj3626

樹剖+線段樹--bzoj3626

傳送門

也是一道思路很好的題。

對於一個點 z z ,和一個標號區間 [ l , r ]

[l,r] ,要求 i = l r d
e p [ L C A ( i , z )
] \sum_{i=l}^rdep[LCA(i,z)]

如果純暴力的話是 n 2 n^2 的,甚至連第一個部分分都拿不到

但是 L C A ( i , z ) LCA(i,z) 一定是 z z r o o t root 的鏈上的某一個點,如果把這些點權值都設成 1 1 ,那麼答案就是 l , r l,r 的每個點到 r o o t root 的鏈上的點權和(有點差分的思想),求鏈上的點權和就可以用樹剖+線段樹來做,但這樣複雜度還是很高。然而這個問題是可以翻過來的,就是對於 [ l , r ] [l,r] 的每個點,把他們到 r o o t root 的鏈上的點權都 + 1 +1 ,則答案就是 z z r o o t root 的這條鏈的點權和。

那就可以把詢問離線下來,把 [ l , r ] [l,r] 變成 [ 1 , r ] [ 1 , l 1 ] [1,r]-[1,l-1] ,點的編號從小到大加進去,然後再處理這個地方的詢問就好了

程式碼如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 50005
#define ls cur<<1
#define rs cur<<1|1
#define len(x) (node[x].r-node[x].l+1)
using namespace std;
const int mod=201314;
 
inline int rd(){
    int x=0,f=1;char c=' ';
    while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
}
 
int n,m,cnt,head[N],tot;
int siz[N],dep[N],dfn[N],rk[N],son[N],top[N],fa[N],num;
 
struct EDGE{
    int to,nxt;
}edge[N<<1];
inline void add(int x,int y){
    edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}
 
void dfs1(int u,int fat){
    siz[u]=1; int maxson=-1;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to; if(v==fat) continue;
        dep[v]=dep[u]+1; fa[v]=u;
        dfs1(v,u); siz[u]+=siz[v];
        if(siz[v]>maxson) maxson=siz[v],son[u]=v;
    } return;
}
void dfs2(int u,int t){
    top[u]=t; dfn[u]=++num; rk[num]=u;
    if(!son[u]) return;
    dfs2(son[u],t);
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(!dfn[v]) dfs2(v,v);
    }
}
 
struct Node{
    int l,r,sum,lazy;
}node[N<<2];
 
inline void pushup(int cur){
    node[cur].sum=node[ls].sum+node[rs].sum;
}
inline void pushdown(int cur){
    if(node[cur].lazy){
        int t=node[cur].lazy; node[cur].lazy=0;
        node[ls].sum+=len(ls)*t; node[ls].lazy+=t;
        node[rs].sum+=len(rs)*t; node[rs].lazy+=t; 
    }
}
 
void build(int cur,int L,int R){
    if(L==R){
        node[cur].l=node[cur].r=L; return;
    }
    int mid=(L+R)>>1;
    build(ls,L,mid); build(rs,mid+1,R);
    node[cur].l=node[ls].l,node[cur].r=node[rs].r;
    pushup(cur); return;
}
 
void update(int cur,int L,int R){
    if(L<=node[cur].l && node[cur].r<=R){
        node[cur].sum+=len(cur); node[cur].lazy++;
        return;
    }
    pushdown(cur);
    int mid=(node[cur].l+node[cur].r)>>1;
    if(L<=mid) update(ls,L,R);
    if(mid<R) update(rs,L,R);
    pushup(cur);
}
 
int query(int cur,int L,int R){
    if(L<=node[cur].l && node[cur].r<=R) return node[cur].sum;
    pushdown(cur);
    int mid=(node[cur].l+node[cur].r)>>1,res=0;
    if(L<=mid) (res+=query(ls,L,R))%=mod;
    if(mid<R) (res+=query(rs,L,R))%=mod;
    return res; 
}
 
inline void change(int x,int y){
    while(top[x]!=top[y]){
        update(1,dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }
    update(1,dfn[y],dfn[x]);
}
 
inline int ask(int x){
    int ans=0;
    while(top[x]!=1){
        ans+=query(1,dfn[top[x]],dfn[x]); ans%=mod;
        x=fa[top[x]];
    }
    ans+=query(1,dfn[1],dfn[x]); ans%=mod;
    return ans;
}
 
struct qwq{
    int id,pos; bool flg;
    bool operator <(const qwq &x) const{
        return pos<x.pos;
    }
}a[N<<1];
 
struct QUE{int z,ans;}q[N];
 
int main(){
    n=rd(); m=rd();
    for(int i=1;i<n;i++){
        int x=rd(); ++x;
        add(x,i+1);
    }
    dfs1(1,1); dfs2(1,1); build(1,1,n);
    for(int i=1;i<=m;i++){
        int l=rd(),r=rd();q[i].z=rd()+1; ++l,++r;
        a[++tot].id=i,a[tot].pos=l-1,a[tot].flg=0;
        a[++tot].id=i,a[tot].pos=r,  a[tot].flg=1;
    }
    sort(a+1,a+tot+1); int now=0;
    for(int i=1;i<=tot;i++){
        while(now<a[i].pos){
            ++now; change(now,1);
        }
        if(!a[i].flg) q[a[i].id].ans-=ask(q[a[i].id].z);
        else q[a[i].id].ans+=ask(q[a[i].id].z);
    }
    for(int i=1;i<=m;i++) printf("%d\n",(q[i].ans+mod)%mod);
    return 0;
}