线段树 ---- 线段树上区间二分 或者单点二分 codeforces C. Greedy Shopping

题目大意


题目大意:

给你一个非增的区间现在你有两次操作

  • 1 x y : 把 [ a 1 , . . . a x ] [a_1,...a_x] [a1,...ax]里面的数对 y y y m a x max max
  • 2 x y : 你有 y y y元,从 x x x n n n,每次遇到一个 a i a_i ai,如果你比它大你就减掉 a i a_i ai,问你可以减多少次?

MY solution

  1. 一开始看一眼对于第一个操作是没问题的,但是对于第二个操作我们怎么搞?
  2. 假设你能减 x x x次,那么每次减的数是集合 { b } \{b\} {b}满足 b 1 > b 2 > b 3 > b 4..... > b n b_1>b_2>b_3>b4.....>bn b1>b2>b3>b4.....>bn
  3. 而且最关键的一点就是 b 2 > b 1 , b 3 > b 2 + b 1 , b 4 > b 3 + b 2 + b 1 → b 2 = b 1 + 1 , b 3 = 2 ∗ ( b 1 + 1 ) , b 4 = 4 ∗ ( b 1 + 1 ) − 1...... b2>b1,b3>b2+b1,b4>b3+b2+b1\rightarrow b2=b1+1,b3=2*(b1+1),b4=4*(b1+1)-1...... b2>b1,b3>b2+b1,b4>b3+b2+b1b2=b1+1,b3=2(b1+1),b4=4(b1+1)1......,那么我们发现,这个增长的速度是斐波那契数列的增长速度的,那么就是 l o g log log级别的
  4. 那么就是对于第二问每次只会修改 l o g n logn logn个区间
  5. 那么这道题线段树可以这么用就是:
  6. 对于第一个操作你线段树维护区间和,区间最小值。你每次查找前缀的区间最小值,你就可以找到一个临界点 k k k, [ k , x ] [k,x] [k,x]里面所有的数都被修改成 y y y
  7. 对于第二个操作就是我一开始是二分套线段树里面再套区间是 l o g 3 n log^3n log3n的T飞了
  8. 想把二分优化掉:就是我是想可不可以在线段树上面二分??有点类似树状数组上面的倍增?
  9. 我们要利用好数组非增的性质,对于 y y y我们先找到一个第一个比 y y y小的位置 p o s pos pos,然后和 x x x m a x max max, 然后算出 [ 1 , p o s ] [1,pos] [1,pos]里面的和是多少,假设是 s u m sum sum,那么我们再去找前缀和为第一个大于等于 s u m + y sum+y sum+y的位置记做 p o s ′ pos' pos,那么区间 [ p o s , p o s ′ ] [pos,pos'] [pos,pos]就是当前 y y y值可以连续减的区间,一直重复就好了
    10.时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}

struct Segtree{
	int tr_min[maxn<<2];
	ll tr_sum[maxn<<2];
	int lazy[maxn<<2];
	int arr[maxn];
	void pushup(int rt) {
		tr_sum[rt] = tr_sum[rt<<1] + tr_sum[rt<<1|1];
		tr_min[rt] = min(tr_min[rt<<1],tr_min[rt<<1|1]);
		return;
	}
	void pushdown(int rt, int l, int r) {
		if(lazy[rt]) {
			tr_min[rt<<1] = lazy[rt];
			tr_min[rt<<1|1] = lazy[rt];
			lazy[rt<<1] = lazy[rt];
			lazy[rt<<1|1] = lazy[rt];
			tr_sum[rt<<1] = 1ll*(mid-l+1)*lazy[rt];
			tr_sum[rt<<1|1] = 1ll*(r-mid)*lazy[rt];
			lazy[rt] = 0; 
		}
	}
	void build(int rt, int l, int r) {
	    lazy[rt] = 0;
		if(l == r) {
			tr_min[rt] = arr[l];
			tr_sum[rt] = arr[l];
			return;
		}
		build(Lson);
		build(Rson);
		pushup(rt);
	}
	void update(int rt, int l, int r, int posl, int posr,int val) {
		if(posl <= l && posr >= r) {
			tr_min[rt] = val;
			tr_sum[rt] = 1ll * (r - l + 1) * val;
			lazy[rt] = val;
			return;
		}
		pushdown(rt,l,r);
		if(posl <= mid) update(Lson,posl,posr,val);
		if(posr > mid) update(Rson,posl,posr,val);
		pushup(rt);
	}
	int getmin(int rt, int l, int r, int posl, int posr) {
		if(posl <= l && posr >= r) return tr_min[rt];
		pushdown(rt,l,r);
		int res = INF;
		if(posl <= mid) res = min(res,getmin(Lson,posl,posr));
		if(posr > mid) res = min(res,getmin(Rson,posl,posr));
		return res; 
	}
	ll getsum(int rt, int l, int r, int posl, int posr) {
		if(posl > posr) return 0;
		if(posl <= l && posr >= r) return tr_sum[rt];
		pushdown(rt,l,r);
		ll res = 0;
		if(posl <= mid) res = res + getsum(Lson,posl,posr);
		if(posr > mid) res = res + getsum(Rson,posl,posr);
		return res; 
	}
	int getpos(int rt, int l, int r, ll val) {
		if(l == r) return l;
		pushdown(rt,l,r);
		if(tr_min[rt<<1] <= val) return getpos(Lson,val);
		if(tr_min[rt<<1|1] <= val) return getpos(Rson,val);
		return INF;
	}
	int getans(int rt, int l, int r, ll val) {
		if(tr_sum[rt] <= val || l == r) return r;
		pushdown(rt,l,r);
		if(tr_sum[rt<<1] >= val) return getans(Lson,val);
		else return getans(Rson,val-tr_sum[rt<<1]);
	}
}sgt;
int main() {
	IOS;
	int n, q;
	cin >> n >> q;
	for(int i = 1; i <= n; ++ i) cin >> sgt.arr[i];
	sgt.build(1,1,n);
	while(q --) {
		int op, x;
        ll y;
		cin >> op >> x >> y;
		if(op == 1) {
			int l = 1, r = x;
			while(l < r) {
				if(sgt.getmin(1,1,n,1,mid) > y) l = mid + 1;
				else r = mid;
			}
			if(sgt.getmin(1,1,n,1,l) <= y) sgt.update(1,1,n,l,x,y);
		} else {
			int ans = 0;
			while(y) {
				x = max(sgt.getpos(1,1,n,y),x); // 获取第一个比y小等于的位置
				if(x > n) break;
				ll val = sgt.getsum(1,1,n,1,x-1);// 计算位置的前的和注意这里是x-1
				int pos = sgt.getans(1,1,n,val+y);//找位置
				if(sgt.getsum(1,1,n,1,pos) > val+y) pos --;// 如果不是刚好等就要往前一位 
				y -= sgt.getsum(1,1,n,1,pos) - val;
				ans += (pos - x + 1);
				x = pos + 1;
				if(x > n) break;
			}
			cout << ans << endl;
		}
	}
}

others solution

代码来源
还有其实我们是可以直接在线段树上面找到目标区间再进行二分递归修改的,这样非常好写时间也是一样的

#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
 
inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}
 
const int MAXN = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
 
int n, q;
 
int ans;

int arr[MAXN];
 
struct Node
{
    int l, r;
    int sum, minV, maxV;
    int f;
}tree[MAXN * 4];

void pushUp(int k)
{
    tree[k].sum = tree[2 * k].sum + tree[2 * k + 1].sum;
    tree[k].minV = min(tree[2 * k].minV, tree[2 * k + 1].minV);
    tree[k].maxV = max(tree[2 * k].maxV, tree[2 * k + 1].maxV);
}

void down(int k)
{
    tree[2 * k].f = max(tree[2 * k].f, tree[k].f);
    tree[2 * k].minV = tree[2 * k].maxV = tree[2 * k].f;
    tree[2 * k].sum = (tree[2 * k].r - tree[2 * k].l + 1) * tree[k].f;
    tree[2 * k + 1].f = max(tree[2 * k + 1].f, tree[k].f);
    tree[2 * k + 1].minV = tree[2 * k + 1].maxV = tree[2 * k + 1].f;
    tree[2 * k + 1].sum = (tree[2 * k + 1].r - tree[2 * k + 1].l + 1) * tree[2 * k + 1].f;
    tree[k].f = 0;
}

void build(int l, int r, int k)
{
    tree[k].l = l, tree[k].r = r;
    tree[k].sum = tree[k].maxV = 0, tree[k].minV = 1e18;
    tree[k].f = 0;
    if (l == r)
    {
        arr[l] = read();
        tree[k].sum = arr[l];
        tree[k].maxV = max(tree[k].maxV, arr[l]);
        tree[k].minV = min(tree[k].minV, arr[l]);
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, 2 * k);
    build(mid + 1, r, 2 * k + 1);
    pushUp(k);
}

void change_interval(int k, int l, int r, int c)
{
    if (tree[k].l >= l && tree[k].r <= r)
    {
        // 无效修改
        if (tree[k].minV >= c)
        {
            return;
        }
        // 区间修改
        else if (tree[k].maxV <= c)
        {
            tree[k].minV = tree[k].maxV = c;
            tree[k].f = max(tree[k].f, c);
            tree[k].sum = (tree[k].r - tree[k].l + 1) * c;
        }
        // 进一步细分
        else
        {
            if (tree[k].f) down(k);
            change_interval(2 * k, l, r, c);
            change_interval(2 * k + 1, l, r, c);
            pushUp(k);
        }
        return;
    }
    if (tree[k].f) down(k);
    int mid = (tree[k].l + tree[k].r) >> 1;
    if (mid >= l) change_interval(2 * k, l, r, c);
    if (r > mid) change_interval(2 * k + 1, l, r, c);
    pushUp(k);
}

void ask_interval(int k, int l, int r, int &money)
{
    if (tree[k].l >= l && tree[k].r <= r)
    {
        // 一个区间中的饭店,能全部吃就全部吃
        if (tree[k].sum <= money)
        {
            money -= tree[k].sum;
            ans += tree[k].r - tree[k].l + 1;
        }
        // 不能全部吃,考虑是否存在吃得起的饭店
        else if (tree[k].minV <= money)
        {
            if (tree[k].f) down(k);
            ask_interval(2 * k, l, r, money);
            ask_interval(2 * k + 1, l, r, money);
        }
        return;
    }
    if (tree[k].f) down(k);
    int mid = (tree[k].l + tree[k].r) >> 1;
    if (mid >= l) ask_interval(2 * k, l, r, money);
    if (r > mid) ask_interval(2 * k + 1, l, r, money);
}

signed main()
{
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    n = read(), q = read();
    build(1, n, 1);
    while (q--)
    {
        int t, x, y;
        t = read(), x = read(), y = read();
        if (t == 1)
        {
            change_interval(1, 1, x, y);
        }
        else
        {
            ans = 0;
            ask_interval(1, x, n, y);
            cout << ans << endl;
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值