【jzoj4895】【三部曲】【线段树】

题目大意

因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量为0。当城市 被加派了k名士兵时。城市i的所有子城市需要被加派k+1名士兵。这些子城市的所有子城市需要被加派k+2名士兵。以此类推。当然,加派士兵的同时,国王也需要不断了解当前的情况。于是他随时可能询问以城市i为根的子树中的所有城市共被加派了多少士兵。你现在是国王的军事大臣,你能回答出国王的每个询问么?

解题思路

先按dfs序重标号,把操作拆成两个,把一段点都加上深度,把一段点都加上一个值,这样就可以用线段树维护了。

code

#include<cmath>
#include<cstdio>
#include<algorithm>
#define LL long long
#define fo(i,j,k) for(LL i=j;i<=k;i++)
#define fd(i,j,k) for(LL i=j;i>=k;i--)
using namespace std;
LL const maxn=5*1e4;
LL n,p,gra,time,begin[maxn+10],to[maxn+10],next[maxn+10],dfn[maxn+10],leave[maxn+10],
dep[maxn+10],tag[maxn*4+10][3];
LL ans,sumdep[maxn*4+10],t[maxn*4+10];
void insert(LL u,LL v){
    to[++gra]=v;
    next[gra]=begin[u];
    begin[u]=gra;
}
void dfs(LL now){
    dfn[now]=++time;
    for(LL i=begin[now];i;i=next[i]){
        dep[time+1]=dep[dfn[now]]+1;
        dfs(to[i]);
    }
    leave[now]=time;
}
void retag(LL pos,LL l,LL mid,LL r){
    fo(i,1,2){
        if(i==1){
            t[pos*2]+=tag[pos][i]*sumdep[pos*2];
            t[pos*2+1]+=tag[pos][i]*sumdep[pos*2+1];
        }else{
            t[pos*2]+=tag[pos][i]*(mid-l+1);
            t[pos*2+1]+=tag[pos][i]*(r-mid);
        }
        tag[pos*2][i]+=tag[pos][i];
        tag[pos*2+1][i]+=tag[pos][i];
        tag[pos][i]=0;
    }
}
void reset(LL pos,LL l,LL r){
    LL mid=(l+r)/2;
    if(l==r)sumdep[pos]=dep[l];
    else{
        reset(pos*2,l,mid);
        reset(pos*2+1,mid+1,r);
        sumdep[pos]=sumdep[pos*2]+sumdep[pos*2+1];
    }
}
LL qury(LL pos,LL l,LL r,LL ll,LL rr){
    LL mid=(l+r)/2;
    if(l!=r)retag(pos,l,mid,r);
    if((l==ll)&&(r==rr))return t[pos];
    else if(rr<=mid)return qury(pos*2,l,mid,ll,rr);
    else if(mid<ll)return qury(pos*2+1,mid+1,r,ll,rr);
    else return qury(pos*2,l,mid,ll,mid)+qury(pos*2+1,mid+1,r,mid+1,rr);
}
void add(LL pos,LL l,LL r,LL ll,LL rr,LL op,LL val){
    LL mid=(l+r)/2;
    if(l!=r)retag(pos,l,mid,r);
    if((l==ll)&&(r==rr)){
        tag[pos][op]+=val;
        if(op==1)t[pos]+=sumdep[pos];
        else t[pos]+=(r-l+1)*val;
        return;
    }else if(rr<=mid)add(pos*2,l,mid,ll,rr,op,val);
    else if(mid<ll)add(pos*2+1,mid+1,r,ll,rr,op,val);
    else{
        add(pos*2,l,mid,ll,mid,op,val);
        add(pos*2+1,mid+1,r,mid+1,rr,op,val);
    }
    t[pos]=t[pos*2]+t[pos*2+1];
}
int main(){
    //freopen("truetears.in","r",stdin);
    //freopen("truetears.out","w",stdout);
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%lld%lld",&n,&p);
    fo(i,2,n){
        LL u;scanf("%lld",&u);
        insert(u,i);
    }
    dfs(1);
    reset(1,1,n);
    scanf("\n");
    fo(i,1,p){
        char ch;scanf("%c",&ch);
        if(ch=='A'){
            LL x,k;scanf("%lld%lld\n",&x,&k);
            add(1,1,n,dfn[x],leave[x],1,1);
            add(1,1,n,dfn[x],leave[x],2,k-dep[dfn[x]]);
        }else{
            LL x;scanf("%lld\n",&x);
            printf("%lld\n",qury(1,1,n,dfn[x],leave[x]));
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值