Educational Codeforces Round 126 (Rated for Div. 2) D 线段树维护差分数组

题目

给你一个数组A,每次可以指定一个大小为K区间L,R。
使得从L到R的每个数分别减1,2,3,4,5…k。
问至少需要多少次才能让数组的全部数变成非正数。

题解思路

参考pzr佬
参考ygg佬
可以直接贪心的正向考虑,从a1开始这个数肯定要变成非正数,所以我们直接使用a1次将它改变,每次只减1所以得k次。
但是k个区间里的数都要减去对应的数,我们可以都减去1来求前缀(因为上面那个很明显是个公差为1的等差)。我们还得消除这个前缀给之后的点带来的影响,所以得在R+1加上K来抵消影响。

要使用区间加减法可以用上线段树。

这样我们可以从1开始做每次取这个数的前缀加自己来判断是否需要减法。

如果反着想呢?
我们从n开始,取这个数能减的最大的数,以及减多少次,这样我们不就不需要在R+1抵消影响了吗,因为我们是从后往前,后面的数已经考虑过了。

AC代码
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define int long long

using namespace std;

const  int  INF =  0x3f3f3f3f;
const  int  N = 600100;
int n , k ;
long long  a[N] ; 
struct node 
{
    int l , r  ;
    long long sum , lz ; 
}tee[4*N] ; 
void bd(int i , int l , int r )
{
    tee[i].l = l , tee[i].r = r ; 
    if ( l == r )
    {
        tee[i].sum = 0 ;
        tee[i].lz = 0 ;  
        return ; 
    }
    int j = (l+r)/2 ; 
    bd(i*2,l,j);
    bd(i*2+1,j+1,r) ; 
    tee[i].sum = tee[i*2].sum + tee[i*2+1].sum ; 
    tee[i].lz = 0 ; 
}
void pd(int i )
{
    if ( tee[i].lz != 0 )
    {
        tee[i*2].lz += tee[i].lz;
        tee[i*2+1].lz += tee[i].lz;
        tee[i*2].sum += tee[i].lz*(tee[i*2].r - tee[i*2].l + 1 );
        tee[i*2+1].sum += tee[i].lz*(tee[i*2+1].r - tee[i*2+1].l + 1 );
        tee[i].lz = 0 ;
    }
}
long long sea(int i , int l , int r )
{
    if (tee[i].l >= l && tee[i].r <= r )
        return tee[i].sum ;
    pd(i);
    long long sum = 0 ; 
    if ( tee[i*2].r >= l )
        sum += sea(i*2,l,r) ; 
    if ( tee[i*2+1].l <= r )
        sum += sea(i*2+1,l,r) ; 
    return sum ;
}
void add(int i , int l , int r , int p )
{
    if (tee[i].l >= l && tee[i].r <= r )
    {
        tee[i].sum += p*(tee[i].r-tee[i].l+1) ; 
        tee[i].lz += p ; 
        return ; 
    }
    pd(i);
    if ( tee[i*2].r >= l )
        add(i*2,l,r,p) ; 
    if ( tee[i*2+1].l <= r )
        add(i*2+1,l,r,p) ; 
    tee[i].sum = tee[i*2].sum + tee[i*2+1].sum ; 
}
void solve()
{
    cin >> n >> k ;
    for (int i = 1 ; i <= n ; i++ )
        cin >> a[i] ; 
    bd(1,1,n) ; 
    long long ans = 0 ; 
    for (int i = n ; i >= 1 ; i-- )
    {
        long long num = a[i] + sea(1,1,i) ; 
        //cout << sea(1,i,i) << "\n" ; 
        if ( num > 0 )
        {
            int l = max(i - k + 1,1ll) ; 
            int r = l + k - 1 ; 
            long long st = i - l + 1 ; 
            st = (num+st-1)/st ; 
            //cout << st << "\n" ; 
            add(1,l,r,-st) ; 
            ans += st ; 
        }
    }
    cout << ans << "\n" ; 
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        solve() ;
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值