线段树二分 - Luogu P5579 [PA2015] - Siano

线段树二分 - Luogu P5579 [PA2015] - Siano

经过排序后,每次操作都可以转化为一个连续区间。在割草时,只需要查询最小的高于v的草,然后修改从它开始的整个后缀区间。本题难点在于如果查询最小的高于v的草,如果简单的用二分内套线段树单点查询,是logn * logn的复杂度,会T。如果在线段树中维护区间最小草长,可以写出logn的算法来实现这个查询。(见find函数)。

本题一切均建立在这样一个特殊条件下:线段树维护的区间是非递减区间

#include <bits/stdc++.h>
using namespace std;

const int N = 5+10;
typedef long long ll;
int n, m;
ll k, v;
ll a[N];
struct Node{
	ll inc; // 该区间的单日增长
	ll sum; // 该区间的总长度
	ll minv; // 该区间的最小草长 = 左子区间的最小草长 
	ll lazy; // 增长天数
	ll setlazy; // 剪为v
}tr[N<<2];


void build(int rt, int l, int r){
	tr[rt].sum = 0;
	tr[rt].minv = 0;
	tr[rt].lazy = 0;
	tr[rt].setlazy = -1;
	if(l==r){
		tr[rt].inc = a[l];
	}else{
		int mid = l+r>>1;
		build(rt<<1, l, mid);
		build(rt<<1|1, mid+1, r);
		tr[rt].inc = tr[rt<<1].inc + tr[rt<<1|1].inc;
	}
}

void push_up(int rt){
    tr[rt].sum = tr[rt<<1].sum + tr[rt<<1|1].sum;
    tr[rt].minv = tr[rt<<1].minv;
}

void push_down(int rt, int l, int r){
	if(tr[rt].setlazy != -1){
		int mid = l+r>>1;
		tr[rt<<1].sum = (mid-l+1) * tr[rt].setlazy;
		tr[rt<<1|1].sum = (r-mid) * tr[rt].setlazy;
		tr[rt<<1].minv = tr[rt<<1|1].minv = tr[rt].setlazy;
		tr[rt<<1].setlazy = tr[rt<<1|1].setlazy = tr[rt].setlazy;
		tr[rt<<1].lazy = tr[rt<<1|1].lazy = 0;
		tr[rt].setlazy = -1;
	}

	if(tr[rt].lazy){
		int mid = l+r>>1;
		tr[rt<<1].sum += tr[rt<<1].inc * tr[rt].lazy;
		tr[rt<<1|1].sum += tr[rt<<1|1].inc * tr[rt].lazy;
		tr[rt<<1].lazy += tr[rt].lazy;
		tr[rt<<1|1].lazy += tr[rt].lazy;
		tr[rt<<1].minv += a[l] * tr[rt].lazy;
		tr[rt<<1|1].minv += a[mid+1] * tr[rt].lazy;
		tr[rt].lazy = 0;
	}
}

int find(ll v){
	int rt = 1;
	int l = 1, r = n;
	int ans = -1;
	while(l <= r){
		push_down(rt, l, r);
		int mid = l+r>>1;
        if(l==r){
            if(tr[rt].minv > v) ans = l;
            break;
        }
		if(tr[rt<<1|1].minv > v){
			ans = mid+1;
			rt = rt<<1;
			r = mid;
		}else{
			rt = rt<<1|1;
			l = mid+1;
		}
	}
	return ans;
}

ll query(int rt, int l, int r, int ql, int qr){
	if(ql <= l && qr >= r){
		return tr[rt].sum;
	}else{
		push_down(rt, l, r);
		int mid = l+r>>1;
		ll ans = 0;
		if(ql <= mid) ans += query(rt<<1, l, mid, ql, qr);
		if(qr > mid) ans += query(rt<<1|1, mid+1, r, ql, qr);
		return ans;
	}
}

void cut(int rt, int l, int r, int cl, int cr, ll v){
	if(cl <= l && cr >= r){
		tr[rt].sum = v * (r-l+1);
		tr[rt].minv = v;
		tr[rt].lazy = 0;
		tr[rt].setlazy = v;
		// 置位操作会清楚当前节点的+操作
        // 在pushdown的时候先置位再+
		// 如果+lazy不为0,说明是先置位再+
	}else{
		int mid = l+r>>1;
		push_down(rt, l, r);
		if(cl <= mid){
			cut(rt<<1, l, mid, cl, cr, v);
		}
		if(cr > mid){
			cut(rt<<1|1, mid+1, r, cl, cr, v);
		}
        push_up(rt);
	}
}

int main(){
    // freopen("C:\\Users\\13234\\Desktop\\files\\IO\\0.in","r",stdin);
    // freopen("C:\\Users\\13234\\Desktop\\files\\IO\\0.out","w",stdout);
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i){
		scanf("%d", &a[i]);
	}
    sort(a+1, a+1+n);
	build(1, 1, n);
	ll prek = 0;
	while(m--){
		scanf("%lld%lld", &k, &v);
		ll c = k - prek;
		prek = k;
        push_down(1, 1, n);
		tr[1].sum += tr[1].inc * c;
		tr[1].minv += a[1] * c;
		tr[1].lazy += c;
		int p = find(v);
		if(p == -1){
			printf("0\n");
			continue;
		}
		ll res = query(1, 1, n, p, n) - (n-p+1) * v;
		cut(1, 1, n, p, n, v);
		printf("%lld\n", res);
	}
	return 0;
}
posted @ 2021-04-08 11:23  popozyl  阅读(90)  评论(0编辑  收藏  举报