bzoj3891

学长的题解:显然,暴力求解的复杂度是无法承受的。
考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] ? [1, l ? 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n ? 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。

1.树链剖分


#include<cstdio>
#include<cctype>
#include<algorithm>
#define maxn 50001
#define mo 201314
using namespace std;
int n,m,cnt,head[maxn],to[maxn],nex[maxn];
int dep[maxn],son[maxn],a[maxn],id[maxn],top[maxn],siz[maxn],fa[maxn];
struct data{int p,id,z;bool vis;}q[maxn<<1];
struct result{int ans1,ans2;}ans[maxn];
struct tree{int l,r,sum,tag;}tr[maxn<<2];

inline bool operator<(const data a,const data b){return a.p<b.p;}

inline void read(int &x){
    char ch=getchar();x=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
}

void addedge(int u,int v){
    to[++cnt]=v;nex[cnt]=head[u];head[u]=cnt;
}

void dfs1(int x,int f){
    dep[x]=dep[f]+1;fa[x]=f;siz[x]=1;
    int maxson=-1;
    for(int i=head[x];i;i=nex[i]){
        int y=to[i];
        if(y==f)continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(siz[y]>maxson){maxson=siz[y];son[x]=y;}
    }
}

void dfs2(int x,int topf){
    id[x]=++cnt;
    a[cnt]=x;top[x]=topf;
    if(!son[x])return;
    dfs2(son[x],topf);
    for(int i=head[x];i;i=nex[i]){
        int y=to[i];
        if(y==fa[x]||y==son[x])continue;
        dfs2(y,y);
    }
}

void buildtr(int now,int l,int r){
    tr[now].l=l;tr[now].r=r;
    if(l==r)return;
    int mid=(l+r)>>1;
    buildtr(now<<1,l,mid);buildtr(now<<1|1,mid+1,r);
}

void pushdown(int now){
    if(tr[now].l==tr[now].r||!tr[now].tag)return;
    tr[now<<1].sum=(tr[now<<1].sum+(tr[now<<1].r-tr[now<<1].l+1)*tr[now].tag)%mo;tr[now<<1].tag=(tr[now<<1].tag+tr[now].tag)%mo;
    tr[now<<1|1].sum=(tr[now<<1|1].sum+(tr[now<<1|1].r-tr[now<<1|1].l+1)*tr[now].tag)%mo;tr[now<<1|1].tag=(tr[now<<1|1].tag+tr[now].tag)%mo;
    tr[now].tag=0;
}

void updata(int now,int l,int r){
    pushdown(now);
    if(tr[now].l==l&&tr[now].r==r){
        tr[now].tag++;tr[now].sum+=tr[now].r-tr[now].l+1;
        return;
    }
    int mid=(tr[now].l+tr[now].r)>>1;
    if(mid>=r)updata(now<<1,l,r);else
    if(mid<l)updata(now<<1|1,l,r);else{
        updata(now<<1,l,mid);updata(now<<1|1,mid+1,r);
    }
    tr[now].sum=(tr[now<<1].sum+tr[now<<1|1].sum)%mo;//注意这里(l,r)变成(l,mid)(mid+1,r)因此RE了好几次
}

void solve_updata(int x,int y){
    while(top[x]!=top[y]){
        updata(1,id[top[x]],id[x]);
        x=fa[top[x]];
    }
    updata(1,id[y],id[x]);
}

int query(int now,int l,int r){
    pushdown(now);
    if(tr[now].l==l&&tr[now].r==r)return tr[now].sum;
    int mid=(tr[now].l+tr[now].r)>>1;
    if(mid>=r)return query(now<<1,l,r);
    if(mid<l)return query(now<<1|1,l,r);
    return query(now<<1,l,mid)+query(now<<1|1,mid+1,r);//注意这里(l,r)变成(l,mid+1)和(mid+1,r)因此RE了好几次
}

int solve_query(int x,int y){
    int res=0;
    while(top[x]!=top[y]){
        res=(res+query(1,id[top[x]],id[x]))%mo;
        x=fa[top[x]];
    }
    res=(res+query(1,id[y],id[x]))%mo;
    return res;
}

int main(){
    read(n);read(m);
    for(int i=1;i<n;i++){
        int x;read(x);addedge(x,i);
    }
    cnt=0;
    dfs1(0,0);dfs2(0,0);
    cnt=0;
    for(int i=1;i<=m;i++){
        int l,r,z;read(l);read(r);read(z);
        q[++cnt].p=l-1;q[cnt].id=i;q[cnt].z=z;q[++cnt].p=r;q[cnt].id=i;q[cnt].z=z;q[cnt].vis=1;
    }
    sort(q+1,q+cnt+1);
    buildtr(1,1,n);
    int now=-1;
    for(int i=1;i<=cnt;i++){
        while(now<q[i].p)solve_updata(++now,0);
        int id=q[i].id;
        if(!q[i].vis)ans[id].ans1=solve_query(q[i].z,0);else ans[id].ans2=solve_query(q[i].z,0);
    }
    for(int i=1;i<=m;i++)printf("%d\n",(ans[i].ans2-ans[i].ans1+mo)%mo);
}

 


posted @ 2018-05-08 20:58  lnyzo  阅读(123)  评论(0编辑  收藏  举报