P17153 [ICPC 2017 Xi'an R] Arrangement for Contests

题目大意

有一个长度为 \(n\) 的整数序列 \(a\),要从里面选出长度等于 \(k\) 的连续的子段,将该子段每一位减去 \(1\)。保证在任意时刻序列 \(a\) 的每一元素非负。询问最多能进行的操作数。

分析

假设当处理完左端点为 \(i\) 的子段 \([i, i+k-1]\) 后,原数组的第 \(i\) 个位置,从此以后就再也不会被覆盖到了,所以如果我们不去取这段区间,那么就会造成第 \(i\) 个位置的浪费,所以我们选取的区间越靠左就越优,也就是从左往右遍历时能取就取,容易发现这是符合贪心性质的。所以我们的算法就变成了:遍历左端点 \(l\),计算区间的最小值,将区间每一个元素都减去这个最小值,记录答案。

注意到 \(n\le10^5\),我们不能用朴素的方式去维护这个序列每段区间的最小值,必须要用到线段树来维护。

实现

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int T,a[N];
struct SegmentTree{//线段树
    int l,r,add,val;
    #define l(x) tree[x].l
    #define r(x) tree[x].r
    #define add(x) tree[x].add
    #define val(x) tree[x].val
}tree[N*5];
void pushup(int u){
    val(u)=min(val(u*2),val(u*2+1));
}
void pushdown(int u){
    if(add(u)){
        val(u*2)+=add(u);
        val(u*2+1)+=add(u);
        add(u*2)+=add(u);
        add(u*2+1)+=add(u);
        add(u)=0;
    }
}
void modify(int u,int l,int r,int k){//修改操作
    if(l<=l(u)&&r>=r(u)){
        val(u)+=k;
        add(u)+=k;
        return;
    }
    pushdown(u);
    int mid=(l(u)+r(u))/2;
    if(l<=mid)modify(u*2,l,r,k);
    if(r>mid)modify(u*2+1,l,r,k);
    pushup(u);
}
int query(int u,int l,int r){//查询操作
    if(l<=l(u)&&r>=r(u))return val(u);
    pushdown(u);
    int mid=(l(u)+r(u))/2;
    int temp=1e9;
    if(l<=mid)temp=min(temp,query(u*2,l,r));
    if(r>mid)temp=min(temp,query(u*2+1,l,r));
    return temp;
}
void build(int u,int l,int r){
    l(u)=l;r(u)=r;
    if(l==r){
        val(u)=a[l];
        return;
    }
    int mid=(l(u)+r(u))/2;
    build(u*2,l,mid);
    build(u*2+1,mid+1,r);
    pushup(u);
}
signed main(){
    cin>>T;
    while(T--){
        memset(tree,0,sizeof tree);
        int n,k,ans=0;
        cin>>n>>k;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        build(1,1,n);//线段树一定要有棵树
        for(int L=1;L<=n-k+1;L++){//遍历区间的左端点
            int val=query(1,L,L+k-1);//查询区间的最小值
            ans+=val;//记录答案
            modify(1,L,L+k-1,-val);//将区间的每个元素减去这个最小值
        }
        cout<<ans<<'\n';
    }
}
posted @ 2026-07-31 12:07  UltraRT  阅读(10)  评论(0)    收藏  举报