loj2274 「JXOI2017」加法

二分一下,然后从左到右扫描,扫到左端点就把区间 push 到堆里。
每次有点不符合二分的值时,就贪心地选择右端点最远的 add。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int T, n, m, k, aa, dui[200005], din, a[200005], sta[200005], cnt, j, c[200005];
int uuu;
struct Node{
	int lll, rrr;
}nd[200005];
bool cmp(Node x, Node y){
	return x.lll<y.lll;
}
int lb(int x){
	return x&-x;
}
void add(int p, int x){
	for(int i=p; i<=n+1; i+=lb(i))
		c[i] += x;
}
int query(int p){
	int re=0;
	for(int i=p; i; i-=lb(i))
		re += c[i];
	return re;
}
bool chk(int lim){
	memset(c, 0, sizeof(c));
	din = cnt = uuu = 0;
	j = 1;
	for(int i=1; i<=n; i++)
		if(a[i]<lim)
			sta[++cnt] = i;
	for(int i=1; i<=cnt; i++){
		while(j<=m && nd[j].lll<=sta[i]){
			dui[++din] = nd[j].rrr;
			push_heap(dui+1, dui+1+din);
			j++;
		}
		while(a[sta[i]]+query(sta[i])<lim){
			uuu++;
			if(uuu>k || !din)	return false;
			add(sta[i], aa);
			add(dui[1]+1, -aa);
			pop_heap(dui+1, dui+1+din);
			din--;
		}
	}
	return true;
}
int main(){
	cin>>T;
	while(T--){
		scanf("%d %d %d %d", &n, &m, &k, &aa);
		for(int i=1; i<=n; i++)
			scanf("%d", &a[i]);
		for(int i=1; i<=m; i++)
			scanf("%d %d", &nd[i].lll, &nd[i].rrr);
		sort(nd+1, nd+1+m, cmp);
		int l=1, r=120000000, mid, re;
		while(l<=r){
			mid = (l + r) >> 1;
			if(chk(mid))	re = mid, l = mid + 1;
			else	r = mid - 1;
		}
		printf("%d
", re);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/poorpool/p/8582636.html