E. Greedy Shopping(思维+线段树维护区间最值)

https://codeforces.com/contest/1440/problem/E

题意:有n个商店, 去第i个商店购买物品需要花费a[i]元. 其中保证a序列为非增序列

两种操作:
① 1 x y 将[1, x] 区间的a[i]设置为 a[i] = max(a[i], y);
② 2 x y 你有y元钱, 从编号为x的商店出发, 依次遍历所有商店直至到n号商店为止, 如果能购买当前商店的物品则购买. 问你能买多少物品


思路:其实比较明显的线段树,需要一点点思维。开始往线段树上二分,确实可以但是比较麻烦。

首先观察到是一个不递增的序列。所以按照对于操作1,如果当前覆盖到的区间的最大值<=y,那么进行修改,如果当前覆盖到的区间的最小值>y,那么就不修改。(维护最小值是为了剪枝,不然TLE5)然后上lazytag。

对于操作二,区间查询,如果当前区间的最小值>x,说明该区间无需购买。反之,先递归左子树,再递归右子树。如果有区间的区间和<=拥有的钱,那就买下来。不然就不能买,继续递归。维护的时候把钱存进函数的引用里。

写的时候思路不清楚,对线段树理解还要加强。

建议区间查询修改的时候if判断只判区间覆盖是否满足,再在里面进行各种条件的判断,注意此时遍历到叶子节点,需要计算能否买下,同时注意递归边界。

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+100;
typedef long long LL;
LL a[maxn];
struct Tree{
    LL l,r,maxval,minval,sum,tag;///区间最大值,区间最小值,区间和,l
}tree[maxn*4];
void push_up(LL p)
{
    tree[p].maxval=max(tree[p*2].maxval,tree[p*2+1].maxval);
    tree[p].minval=min(tree[p*2].minval,tree[p*2+1].minval);
    tree[p].sum=tree[p*2].sum+tree[p*2+1].sum;
}
void addtag(LL p,LL d)
{
    tree[p].tag=d;
    tree[p].sum=tree[p].tag*(tree[p].r-tree[p].l+1);
    tree[p].maxval=tree[p].tag;
    tree[p].minval=tree[p].tag;
}
void push_down(LL p)
{
    if(tree[p].tag!=-1){
        addtag(p*2,tree[p].tag);
        addtag(p*2+1,tree[p].tag);
        tree[p].tag=-1;
    }
}
void build(LL p,LL l,LL r)
{
    tree[p].l=l;tree[p].r=r;tree[p].maxval=-1e18;tree[p].minval=1e18;
    tree[p].sum=0;tree[p].tag=-1;
    if(l==r) {tree[p].maxval=tree[p].minval=tree[p].sum=a[l];return;}
    LL mid=(l+r)>>1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    push_up(p);
}
void modify(LL p,LL l,LL r,LL d)
{
    if(l<=tree[p].l&&r>=tree[p].r)///要修改的区间涵盖在区间内部
    {
        if(tree[p].minval>=d) return;///不剪枝会TLE5
        if(tree[p].maxval<d)
        {
            addtag(p,d);        return;
        }
        if(tree[p].l==tree[p].r) return;
    }
    push_down(p);
    LL mid=(tree[p].l+tree[p].r)>>1;
    if(l<=mid) modify(p*2,l,r,d);
    if(r>mid) modify(p*2+1,l,r,d);
    push_up(p);
}
LL query(LL p,LL l,LL r,LL &x)
{
    if(l<=tree[p].l&&r>=tree[p].r)
    {
        if(tree[p].minval>x) return 0;
        if(x>=tree[p].sum){
            x-=tree[p].sum;
            return tree[p].r-tree[p].l+1;
        }
        if(tree[p].l==tree[p].r) return 0;///返回到叶子节点的边界,注意叶子节点要考虑付钱
    }
    LL ans=0;
    push_down(p);
    LL mid=(tree[p].l+tree[p].r)>>1;
    if(l<=mid) ans+=query(p*2,l,r,x);
    if(r>mid) ans+=query(p*2+1,l,r,x);
    return ans;
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n,m;cin>>n>>m;
  for(LL i=1;i<=n;i++) cin>>a[i];
  build(1,1,n);
  while(m--)
  {
      LL op,l,r;cin>>op>>l>>r;
      if(op==1){
        modify(1,1,l,r);
      }
      else if(op==2){
        cout<<query(1,l,n,r)<<endl;
      }
  }
return 0;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/109824665
今日推荐