Greedy Shopping (Codeforces)

题目

You are given an array a1,a2,…,ana1,a2,…,an of integers. This array is non-increasing.

Let's consider a line with nn shops. The shops are numbered with integers from 11 to nn from left to right. The cost of a meal in the ii-th shop is equal to aiai.

You should process qq queries of two types:

  • 1 x y: for each shop 1≤i≤x1≤i≤x set ai=max(ai,y)ai=max(ai,y).
  • 2 x y: let's consider a hungry man with yy money. He visits the shops from xx-th shop to nn-th and if he can buy a meal in the current shop he buys one item of it. Find how many meals he will purchase. The man can buy a meal in the shop ii if he has at least aiai money, and after it his money decreases by aiai.

Input

The first line contains two integers nn, qq (1≤n,q≤2⋅1051≤n,q≤2⋅105).

The second line contains nn integers a1,a2,…,ana1,a2,…,an (1≤ai≤109)(1≤ai≤109) — the costs of the meals. It is guaranteed, that a1≥a2≥…≥ana1≥a2≥…≥an.

Each of the next qq lines contains three integers tt, xx, yy (1≤t≤21≤t≤2, 1≤x≤n1≤x≤n, 1≤y≤1091≤y≤109), each describing the next query.

It is guaranteed that there exists at least one query of type 22.

Output

For each query of type 22 output the answer on the new line.

思路&&题解

这场比赛没有打...

首先看这个东西就很像线段树,但是第二个操作应该怎么办??其实暴力线段树是可以直接过掉的,首先从左往右找到第一个比y小的数的位置l,然后以l为左端点向右找到最大区间比y小,然后直接修改y即可

这样看似很暴力其实时间复杂度是O(nlogn^2)的。因为线段树也是二叉树,所以用启发式合并的思想,如果找到的一个左端点的值大于y/2,那么y就会减半,如果左端点的值是小于y/2,又因为序列单调不上升,所以一定找得到一段区间使得这段区间值和大于y/2,因此就是logn次了

其实这道题更难搞的是写法问题,难点是对于给定区间在线段树上直接二分,可以利用线段树是从左往右依次访问的性质,所以当发现一段合法区间的最小值小于y时,那么就只递归这一段区间,后面的区间就可以不管了

然后由于自己码力不够,一个晚上过去了....

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 2e5 + 3;
struct tree{
    int l , r , minn , lazy;
    ll sum;
}tre[MAXN<<2];
int n , m ;
ll a[MAXN];
void build( int i , int l , int r ){
    tre[i].l = l , tre[i].r = r;tre[i].lazy = 0;
    if( l == r ){
        tre[i].minn = a[l];tre[i].sum = a[l];
        return ;
    }
    int mid = l + r >> 1;
    build( i << 1 , l , mid );
    build( i << 1 | 1 , mid + 1 , r ) ;
    tre[i].minn = min( tre[i<<1].minn , tre[i<<1|1].minn );
    tre[i].sum = tre[i<<1].sum + tre[i<<1|1].sum;
}
void pushdown( int i ){
    if( tre[i].lazy ){
        tre[i<<1].minn = tre[i].lazy , tre[i<<1|1].minn = tre[i].lazy;
        tre[i<<1].lazy = tre[i<<1|1].lazy = tre[i].lazy;
        tre[i<<1].sum = 1ll * ( tre[i<<1].r - tre[i<<1].l + 1 ) * tre[i].lazy ;
        tre[i<<1|1].sum = 1ll * ( tre[i<<1|1].r - tre[i<<1|1].l + 1 ) * tre[i].lazy ;
        tre[i].lazy = 0;
    }
}
int find_( int i , int l , int r , ll x ){
    if( tre[i].l >= l && tre[i].r <= r ){
        if( tre[i].minn > x ) return 0;
        if( tre[i].l == tre[i].r ){
            if( tre[i].minn <= x )
                return tre[i].r;
            return 0;
        }
        pushdown( i );
        tre[i].minn = min( tre[i<<1].minn , tre[i<<1|1].minn );
        tre[i].sum = tre[i<<1].sum + tre[i<<1|1].sum;
        if( tre[i<<1].minn > x ) return find_( i << 1 | 1 , l , r , x );
        return find_( i << 1 , l , r , x );
    }
    if( tre[i].l == tre[i].r ) return 0;
    pushdown( i );
    int ans = 0;
    if( tre[i<<1].r >= l )
        ans = find_( i << 1 , l , r , x );
    if( !ans && tre[i<<1|1].l <= r ){
        ans = find_( i << 1 | 1 , l , r , x ) ;
    }
    tre[i].minn = min( tre[i<<1].minn , tre[i<<1|1].minn );
    tre[i].sum = tre[i<<1].sum + tre[i<<1|1].sum;
    return ans;
}
void modify( int i , int l , int r , int delta ){
    if( tre[i].l > r || tre[i].r < l ) return ;
    if( tre[i].l >= l && tre[i].r <= r){
        tre[i].lazy = delta;
        tre[i].minn = delta;
        tre[i].sum = 1ll * ( tre[i].r - tre[i].l + 1 ) * delta;
        return ;
    }
    pushdown( i );
    modify( i << 1 , l , r , delta );
    modify( i << 1 | 1 , l , r , delta );
    tre[i].minn = min( tre[i<<1].minn , tre[i<<1|1].minn );
    tre[i].sum = tre[i<<1].sum + tre[i<<1|1].sum;
}
bool flag;
void que( int i , ll &delta , int &tot ){
    if( tre[i].l == tre[i].r ){
        if( delta >= tre[i].sum )
        tot = max( tot , tre[i].r ) , delta -= tre[i].sum;
        return ;
    }
    pushdown( i );
    tre[i].minn = min( tre[i<<1].minn , tre[i<<1|1].minn );
    tre[i].sum = tre[i<<1].sum + tre[i<<1|1].sum;
    if( tre[i<<1].sum <= delta ) {
        tot = max( tot , tre[i<<1].r );
        delta -= tre[i<<1].sum;
        que( i << 1 | 1 , delta , tot );
    }
    else{
        que( i << 1 , delta , tot );
    }
}
void query( int i , int l , int r , ll &delta , int &tot ){
    if( tre[i].l > r || tre[i].r < l || flag )return ;
    if( tre[i].l >= l && tre[i].r <= r ){
        if( tre[i].sum <= delta ){ tot = max( tre[i].r , tot );delta -= tre[i].sum;return ;}
        if( tre[i].l == tre[i].r ){
            if( tre[i].sum <= delta ){ tot = max( tot , tre[i].r ) , delta -= tre[i].sum;}
            else flag = 1;
            return ;
        }
        flag = 1;
        que( i , delta , tot );
        pushdown( i );
        tre[i].minn = min( tre[i<<1].minn , tre[i<<1|1].minn );
        tre[i].sum = tre[i<<1].sum + tre[i<<1|1].sum;
        return ;
    }
    pushdown( i );
    tre[i].minn = min( tre[i<<1].minn , tre[i<<1|1].minn );
    tre[i].sum = tre[i<<1].sum + tre[i<<1|1].sum;
    query( i << 1 , l , r , delta , tot );
    query( i << 1 | 1 , l , r , delta , tot );
}
int main(){
    scanf( "%d%d" , &n , &m );
    for( int i = 1 ; i <= n ; i ++ ) scanf( "%lld" , &a[i] );
    build( 1 , 1 , n );
    for( int i = 1 ; i <= m ; i ++ ){
        int x , op;ll y;scanf( "%d%d%lld" , &op , &x , &y );
        if( op == 1 ){
            int tot = find_( 1 , 1 , x , y );
            if( !tot ) continue;
            modify( 1 , tot , x , y );
            //int tot1 = query( 1 , tot , n , y );
        }
        else{
            int an =0 ;
            while( x <= n ){
                int tot = find_( 1 , x , n , y );
                int tot1 = 0;flag = 0;
                query( 1 , tot , n , y , tot1 );
                if( tot == 0 || tot1 == 0 ) break;
                an += tot1 - tot + 1 , x = tot1 + 1;
            }
            printf( "%d\n" , an );
        }
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值