codeforces1440 E. Greedy Shopping

昨天晚上做完4题还有30分钟,感觉太晚了就没继续写,不过看了下E题感觉是一个线段树题目,今天中午看了看发现就是一个线段树上递归的询问问题,不过我之前没写过但是靠着日益强大的乱写能力竟然水出来了~~

E. Greedy Shopping

不难知道操作1并不改变原数组不升序的性质即非严格单调递减的性质永远存在。

操作一:在线段树上二分第一个小于y的数的位置pos,然后区间修改即可 [ p o s → x ] [pos\to x] [posx]
操作二:维护一个区间最小值和区间和,然后递归乱搞,由于每次能买则买先往左子树递归,然后记录一下左子树的花费,再往右子树递归这时候剩余的钱要减去左子树的花费,全局变量记录答案。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<random>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=400010;
ll a[N];
int n,q;
struct node
{
    int l,r;
    ll sum,mn;
    ll lazy;
}tree[N*4];
void pushup(int u)
{
    tree[u].sum=tree[u<<1].sum+tree[u<<1|1].sum;
    tree[u].mn=min(tree[u<<1].mn,tree[u<<1|1].mn);
}
void pushdown(int u)
{
    if(!tree[u].lazy)return;
    
    tree[u<<1].sum=(tree[u<<1].r-tree[u<<1].l+1)*tree[u].lazy;
    tree[u<<1|1].sum=(tree[u<<1|1].r-tree[u<<1|1].l+1)*tree[u].lazy;

    tree[u<<1].mn=tree[u<<1|1].mn=tree[u].lazy;
    tree[u<<1].lazy=tree[u<<1|1].lazy=tree[u].lazy;
    tree[u].lazy=0;
}
void build(int u,int l,int r)
{
    tree[u]={l,r};
    if(l==r)  
    {
        tree[u].sum=tree[u].mn=a[l];
        return;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    pushup(u);
}
void modify(int u,int l,int r,ll val)
{
    if(tree[u].l>=l&&tree[u].r<=r)
    {
        tree[u].lazy=tree[u].mn=val;
        tree[u].sum=(tree[u].r-tree[u].l+1)*val;
        return;
    }
    pushdown(u);
    int mid=tree[u].l+tree[u].r>>1;
    if(l<=mid) modify(u<<1,l,r,val);
    if(r>mid)  modify(u<<1|1,l,r,val);
    pushup(u);
}
int findmn(int u,ll val)
{
    if(tree[u].l==tree[u].r) return tree[u].l;
    pushdown(u);
    if(tree[u<<1].mn>=val) return findmn(u<<1|1,val);
    else return findmn(u<<1,val);
}
int ans;
int calc(int u,int l,int r,ll now)
{
    if(tree[u].r<l||tree[u].l>r||!now) return 0;
    if(tree[u].l>=l&&tree[u].r<=r)
    {
        if(tree[u].sum<=now)
        {
            ans+=tree[u].r-tree[u].l+1;
            return tree[u].sum;
        }
    }
    ll w=0;
    pushdown(u);
    int mid=tree[u].l+tree[u].r>>1;
    if(l<=mid&&tree[u<<1].mn<=now) w+=calc(u<<1,l,r,now);
    if(r>mid) w+=calc(u<<1|1,l,r,now-w);
    return w;
}
int main()
{
    IO;
    int T=1;
    //cin>>T;
    while(T--)
    {
        cin>>n>>q;
        for(int i=1;i<=n;i++) cin>>a[i];
        build(1,1,n+1);
        while(q--)
        {
            int op,x,y;
            cin>>op>>x>>y;
            if(op==1)
            {
                int pos=findmn(1,y);
                if(pos<=x) modify(1,pos,x,y); 
            }
            else
            {
                ans=0;
                calc(1,x,n,y);
                cout<<ans<<'\n';
            }
        }
    }
    return 0;
}

此代码必须在多开一倍空间,要不然calc函数越界?我也不知道为啥很迷
要加哟哦~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值