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;
}