三部曲

题目大意

对一颗n个节点的树操作。
有两种操作,第一种是对子树j,j的点权+k,j的儿子们点权+k+1,j的孙子们点权+k+2,以此类推。
第二种操作询问子树点权和。

线段树

修改操作实际上是对子树每个点i加上k-d[j]+d[i]
标记可以写成(a,b)表示一个点权为x的点i经过后变成x+a+d[i]*b
显然这个标记可合并而且容易求和。
线段树维护即可。

#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=50000+10;
ll tree[maxn*4],sum[maxn*4],a[maxn*4],b[maxn*4];
int h[maxn],go[maxn],nxt[maxn],d[maxn],dfn[maxn],nfd[maxn],size[maxn];
int i,j,k,l,t,n,m,tot,top;
char ch;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void add(int x,int y){
    go[++tot]=y;
    nxt[tot]=h[x];
    h[x]=tot;
}
void dfs(int x){
    dfn[x]=++top;
    nfd[top]=x;
    int t=h[x];
    size[x]=1;
    while (t){
        d[go[t]]=d[x]+1;
        dfs(go[t]);
        size[x]+=size[go[t]];
        t=nxt[t];
    }
}
void build(int p,int l,int r){
    a[p]=b[p]=0;
    if (l==r){
        sum[p]=d[nfd[l]];
        tree[p]=0;
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    sum[p]=sum[p*2]+sum[p*2+1];
}
char get(){
    char ch=getchar();
    while (ch!='A'&&ch!='Q') ch=getchar();
    return ch;
}
void mark(int p,int l,int r,ll c,ll d){
    tree[p]+=(ll)c*(r-l+1)+d*sum[p];
    a[p]+=c;
    b[p]+=d;
}
void down(int p,int l,int r){
    int mid=(l+r)/2;
    if (a[p]||b[p]){
        mark(p*2,l,mid,a[p],b[p]);
        mark(p*2+1,mid+1,r,a[p],b[p]);
        a[p]=b[p]=0;
    }
}
void change(int p,int l,int r,int a,int b,ll c,ll d){
    if (l==a&&r==b){
        mark(p,l,r,c,d);
        return;
    }
    down(p,l,r);
    int mid=(l+r)/2;
    if (b<=mid) change(p*2,l,mid,a,b,c,d);
    else if (a>mid) change(p*2+1,mid+1,r,a,b,c,d);
    else{
        change(p*2,l,mid,a,mid,c,d);
        change(p*2+1,mid+1,r,mid+1,b,c,d);
    }
    tree[p]=tree[p*2]+tree[p*2+1];
}
ll query(int p,int l,int r,int a,int b){
    if (l==a&&r==b) return tree[p];
    down(p,l,r);
    int mid=(l+r)/2;
    if (b<=mid) return query(p*2,l,mid,a,b);
    else if (a>mid) return query(p*2+1,mid+1,r,a,b);
    else return query(p*2,l,mid,a,mid)+query(p*2+1,mid+1,r,mid+1,b);
}
int main(){
    freopen("truetears.in","r",stdin);freopen("truetears.out","w",stdout);
    n=read();m=read();
    fo(i,2,n){
        j=read();
        add(j,i);
    }
    d[1]=1;
    dfs(1);
    build(1,1,n);
    while (m--){
        ch=get();
        if (ch=='A'){
            j=read();k=read();
            change(1,1,n,dfn[j],dfn[j]+size[j]-1,k-d[j],1);
        }
        else{
            j=read();
            printf("%lld\n",query(1,1,n,dfn[j],dfn[j]+size[j]-1));
        }
    }
}

你可能感兴趣的:(dfs序,线段树)