[bzoj5321] [Jxoi2017]加法

Description

可怜有一个长度为 n 的正整数序列 A,但是她觉得 A 中的数字太小了,这让她很不开心。

于是她选择了 m 个区间 [li, ri] 和两个正整数 a, k。她打算从这 m 个区间里选出恰好 k 个区间,并对每个区间执行一次区间加 a 的操作。(每个区间最多只能选择一次。)

对区间 [l, r] 进行一次加 a 操作可以定义为对于所有 i ∈ [l, r],将 Ai 变成 Ai + k。现在可怜想要知道怎么选择区间才能让操作后的序列的最小值尽可能的大,即最大化min Ai

Input

第一行输入一个整数表示数据组数。

对于每组数据第一行输入四个整数 n, m, k, a。

第二行输入 n 个整数描述序列 A。

接下来 m 行每行两个整数 li, ri 描述每一个区间。数据保证所有区间两两不同.

Output

对于每组数据输出一个整数表示操作后序列最小值的最大值。

Sample Input

1 
3 3 2 1
1 3 2
1 1
1 3
3 3

Sample Output

3

HINT

对于100%的数据,保证\(1≤n,m≤200, 1\leq T\leq 2000, 1 ≤ k ≤ m, 1 ≤ a ≤ 100,A_i\le 10^8\)

Solution

二分乱搞。

最小值最大显然二分。

考虑\(check\)怎么写,把区间按\(l\)关键字排序之后按\(r\)关键字用堆维护,然后乱搞。树状数组维护区间加法。

复杂度大概是\(O(Tn\log^2n)\)

话说数据范围好奇怪啊

数组开\(2\times 10^5\),不然莫名\(RE\)(怀疑数据范围)

写起来很简单的啦

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

void read(int &x) {
	x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
    if(x<0) x=-x,putchar('-');
    if(!x) return ;print(x/10);putchar(x%10+'0');
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 2e5+1;

int s[maxn],n,m,k,a;
struct data{
	int l,r;
	int operator < (const data &rhs) const {return l<rhs.l;}
}in[maxn];

struct Binary_Indexed_Tree {
	int tr[maxn];
	void modify(int x,int v) {for(int i=x;i<=n;i+=i&-i) tr[i]+=v;}
	int query(int x,int ans=0) {for(int i=x;i;i-=i&-i) ans+=tr[i];return ans;}
	void clear() {memset(tr,0,sizeof tr);}
}BIT;

priority_queue<int > q;

int check(int lim) {
	BIT.clear();int times=k;
	for(int i=1;i<=n;i++) BIT.modify(i,s[i]),BIT.modify(i+1,-s[i]);
	while(!q.empty()) q.pop();
	int p=1;
	for(int i=1;i<=n;i++) {
		while(p<=m&&in[p].l==i) q.push(in[p].r),p++;
		int x=BIT.query(i);
		if(x>=lim) continue;
		while(x<lim) {
			x+=a;if(q.empty()||q.top()<i) return false;
			int r=q.top();q.pop();
			BIT.modify(i,a),BIT.modify(r+1,-a);times--;
			if(times<0) return false;
		}
	}
	return true;
}

void solve() {
	int l=0,r=0;
	read(n),read(m),read(k),read(a);
	for(int i=1;i<=n;i++) read(s[i]),r=max(r,s[i]);
	for(int i=1;i<=m;i++) read(in[i].l),read(in[i].r);
	sort(in+1,in+m+1);int ans=0;
	r+=k*a;
	while(l<=r) {
		int mid=((l+r)>>1);
		if(check(mid)) ans=max(ans,mid),l=mid+1;
		else r=mid-1;
	}write(ans);
}

int main() {
	int t;read(t);
	while(t--) solve();
	return 0;
}
posted @ 2018-12-23 13:41  Hyscere  阅读(242)  评论(0编辑  收藏  举报