三部曲

题目:

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

输入输出格式
输入格式:
第一行,包含两个整数N,P代表城市数量以及国王的命令的数量。 第二行N−1个整数,表示2−N号每个节点的父亲节点。 接下来的P行,每行代表国王的一个命令,命令分两种:
A X K 在城市X加入K个士兵
Q X 询问以城市X为根的子树中所有士兵数量的和。

输出格式:
对于每个Q,输出答案。

输入输出样例
输入样例#1: 
7 10
1 1 2 2 5 5
Q 1
A 2 1
Q 1
Q 2
Q 5
A 5 0
Q 5
A 3 1
Q 1
Q 2
输出样例#1: 
0
11
11
8
10
14
13
说明
对于50%的数据,1≤N≤1000,1≤P≤300。
对于100%的数据,1≤N≤50000, 1≤P≤100000, 1≤X≤N, 0≤K≤1000

思路:
先处理出每个节点的dfs序、子树大小sz、深度d,再根据dfs序处理出深度前缀和sum。
根据dfs序的性质,可以得到,每个节点和它子树的位置是连续的,那么对一个节点x子树的操作相当于在基于dfs序的序列中的 [ x , x+sz[x]-1 ] 进行操作。
对于线段数中的每个节点Node,使用两个lazy tag。
其中lzy1代表需要加的数x减去操作的根的深度,lzy2代表当前节点被操作的次数。
push_down函数:

a[lson].lzy1+=a[o].lzy1;
a[rson].lzy1+=a[o].lzy1;
a[lson].lzy2+=a[o].lzy2;
a[rson].lzy2+=a[o].lzy2;
a[lson].x+=a[o].lzy2*(tr[mid].sum-tr[l-1].sum)+a[o].lzy1*(mid-l+1);
a[rson].x+=a[o].lzy2*(tr[r].sum-tr[mid].sum)+a[o].lzy1*(r-mid);
a[o].lzy1=a[o].lzy2=0;

代码:

#include<bits/stdc++.h>
using namespace std;

#define lson (o*2)
#define rson (o*2+1)
#define mid (l+(r-l)/2)
#define lhalf lson,l,mid
#define rhalf rson,mid+1,r
#define Out l>Q||r<P
#define Include P<=l&&r<=Q

#define maxn 50000
#define ll long long

struct Node {
    ll x;
    int sz,dic,d;
    ll sum;
    ll lzy1,lzy2;
};

int pre[maxn+5];
int cnt=0;

int n,m;
int fa[maxn+5];
vector<int> g[maxn+5];

Node tr[maxn+5];
Node a[maxn*10+5];

int P,Q;

void readc(char& x) {
    while(!isalpha(x)) x=getchar();
}

void read(int& x) {
    scanf("%d",&x);
}

void readin() {
    read(n);
    read(m);
    for(int i=2; i<=n; i++) {
        read(fa[i]);
        g[fa[i]].push_back(i);
    }
}

void writell(ll x) {
    printf("%lld",x);
}

void writen() {
    printf("\n");
}

void writew() {
    printf(" ");
}

void make_sum() {
    for(int i=1; i<=n; i++) {
        tr[i].sum=tr[i-1].sum+tr[i].d;
    }
}

void push_up(int o) {
    a[o].x=a[lson].x+a[rson].x;
}

void push_down(int o,int l,int r) {
    a[lson].lzy1+=a[o].lzy1;
    a[rson].lzy1+=a[o].lzy1;
    a[lson].lzy2+=a[o].lzy2;
    a[rson].lzy2+=a[o].lzy2;
    a[lson].x+=a[o].lzy2*(tr[mid].sum-tr[l-1].sum)+a[o].lzy1*(mid-l+1);
    a[rson].x+=a[o].lzy2*(tr[r].sum-tr[mid].sum)+a[o].lzy1*(r-mid);
    a[o].lzy1=a[o].lzy2=0;
}

void build(int o,int l,int r) {
    if(l==r) {
        a[o]=tr[l];
        return ;
    }
    build(lhalf),build(rhalf);
}

void update(int o,int l,int r,int x) {
    if(Out) return ;
    if(Include) {
        a[o].lzy1+=(x-tr[P].d);
        a[o].lzy2++;
        a[o].x+=(tr[r].sum-tr[l-1].sum)+x*(r-l+1)-tr[P].d*(r-l+1);
        return ;
    }
    push_down(o,l,r);
    update(lhalf,x),update(rhalf,x);
    push_up(o);
}

void Add() {
    int x,y;
    read(x);
    read(y);
    x=pre[x];
    P=x;
    Q=x+tr[x].sz-1;
    update(1,1,n,y);
}

ll query(int o,int l,int r) {
    if(Out) return 0;
    if(Include) {
        return a[o].x;
    }
    push_down(o,l,r);
    ll s=query(lhalf)+query(rhalf);
    return s; 
}

ll find() {
    int x;
    read(x);
    x=pre[x];
    P=x;
    Q=x+tr[x].sz-1;
    return query(1,1,n);
}

void dfs(int x) {
    pre[x]=++cnt;
    tr[cnt].dic=x;
    tr[cnt].sz=1;
    tr[cnt].d=tr[pre[fa[x]]].d+1;
    for(int i=0; i<g[x].size(); i++) {
        int y=g[x][i];
        dfs(y);
        tr[pre[x]].sz+=tr[pre[y]].sz;
    }
}

int main() {
    readin();
    dfs(1);
    make_sum();
    build(1,1,n);
    while(m--) {
        char x='$';
        readc(x);
        if(x=='A') Add();
        else writell(find()),writen();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值