fzu2171(线段树成段更新)

地址:http://acm.fzu.edu.cn/problem.php?pid=2171

Problem 2171 防守阵地 II

Accept: 32    Submit: 116
Time Limit: 3000 mSec    Memory Limit : 32768 KB

 Problem Description

部队中总共有N个士兵,每个士兵有各自的能力指数Xi,在一次演练中,指挥部确定了M个需要防守的地点,指挥部将选择M个士兵依次进入指定地点进行防守任务,获得的参考指数即为M个士兵的能力之和。随着时间的推移,指挥部将下达Q个指令来替换M个进行防守的士兵们,每个参加完防守任务的士兵由于疲惫等原因能力指数将下降1。现在士兵们排成一排,请你计算出每次进行防守的士兵的参考指数。

 Input

输入包含多组数据。

输入第一行有两个整数N,M,Q(1<=N<=100000,1<=M<=1000,1<=Q<=100000),第二行N个整数表示每个士兵对应的能力指数Xi(1<=Xi<=1000)。

接下来Q行,每行一个整数X,表示在原始队列中以X为起始的M个士兵替换之前的士兵进行防守。(1<=X<=N-M+1)

对于30%的数据1<=M,N,Q<=1000。

 Output

输出Q行,每行一个整数,为每次指令执行之后进行防守的士兵参考指数。

 Sample Input

5 3 32 1 3 1 4123

 Sample Output

635

思路:首现用一般的成段更新代码写了下,提交后发现超时。于是改变下策略,在没一次查询中将变化记录下来,如果下一次需要向下查询时就将记录的变化赋予下一段,然后将变化后的值返回给上一段,这样就行了。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
struct node{
    int l,r,sum,pl;
}tree[100001*4];
void build(int l,int r,int k){
    tree[k].l=l;
    tree[k].r=r;
    tree[k].pl=0;
    if(l==r){
        scanf("%d",&tree[k].sum);
        return ;
    }
    int m=(l+r)/2;
    build(l,m,k*2);
    build(m+1,r,k*2+1);
    tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;
}
int getsum(int l,int r,int k){
    if(l<=tree[k].l&&r>=tree[k].r){
        tree[k].pl++;  //记录变化
        tree[k].sum-=(tree[k].r-tree[k].l+1);
        return tree[k].sum+tree[k].r-tree[k].l+1;
    }
    int m=(tree[k].l+tree[k].r)/2,ans;
    if(tree[k].pl){
        tree[k*2].sum-=(tree[k].pl*(tree[k*2].r-tree[k*2].l+1));  //将变化赋予下一段
        tree[k*2+1].sum-=(tree[k].pl*(tree[k*2+1].r-tree[k*2+1].l+1));
        tree[k*2].pl+=tree[k].pl;  //记录变化,需要注意这里是+=
        tree[k*2+1].pl+=tree[k].pl;
        tree[k].pl=0;
    }
    if(m>=r)
        ans=getsum(l,r,k*2);
    else if(m<l)
        ans=getsum(l,r,k*2+1);
    else
        ans=getsum(l,m,k*2)+getsum(m+1,r,k*2+1);
    tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;  //变化后的值注意返回
    return ans;
}
int main(){
    int n,m,q;
    while(scanf("%d%d%d",&n,&m,&q)>0){
        build(1,n,1);
        while(q--){
            scanf("%d",&n);
            printf("%d\n",getsum(n,n+m-1,1));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值