[JXOI2017]加法

题目链接:[JXOI2017]加法


显然可以二分,然后我们就可以得到每个点需要被多少个区间覆盖。

然后左端点从小到大排序,然后贪心选择即可。

但是有区间覆盖的次数计算,我们可以直接差分,从前往后计算前缀和,用fenwick也可以。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
//#define int long long
using namespace std;
const int N=2e5+10;
int a[N],n,m,K,A,d[N],cnt,pos,l,r;
struct node{int l,r;}t[N];
inline int check(int mid){
	for(int i=1;i<=n;i++)	d[i]=0; pos=1; cnt=0; 
	priority_queue<int> q; while(q.size()) q.pop();
	for(int i=1;i<=n;i++){
		while(pos<=m&&t[pos].l<=i)	q.push(t[pos++].r); d[i]+=d[i-1];
		int tmp=(mid-d[i]-a[i]+A-1)/A;
		while(tmp>0){
			if(!q.size()||q.top()<i)	return 0;
			d[i]+=A,d[q.top()+1]-=A;	q.pop(); tmp--; cnt++;
		}
	}
	return cnt<=K;
}
inline void solve(){
	scanf("%d %d %d %d",&n,&m,&K,&A); l=1e9;
	for(int i=1;i<=n;i++)	scanf("%d",&a[i]),l=min(l,a[i]);
	for(int i=1;i<=m;i++)	scanf("%d %d",&t[i].l,&t[i].r);
	sort(t+1,t+1+m,[](node a,node b){return a.l<b.l;});
	r=l+K*A;
	while(l<r){
		int mid=l+r+1>>1;
		if(check(mid))	l=mid;
		else r=mid-1;
	}
	printf("%d\n",l);
}
signed main(){
	int T; cin>>T; while(T--) solve();
	return 0;
}

你可能感兴趣的:(贪心,堆,二分)