某考试 T3 C

找不着原题了。

原题大概就是给你一条直线上n个点需要被覆盖的最小次数和m条需要花费1的线段的左右端点和1条[1,n]的每次花费为t的大线段。

问最小花费使得所有点的覆盖数都达到最小覆盖数。

感觉这个函数的斜率是单调的,所以就码了一个二分斜率。

#include<bits/stdc++.h>
#define ll long long
#define maxn 100005
using namespace std;
const ll inf=1ll<<62ll;
struct lines{
	int l,r;
	bool operator <(const lines &U)const{
		return l==U.l?r>U.r:l<U.l;
	}
}a[maxn],b[maxn];
int n,m,t,rig,p[maxn];
int le,ri,mid,an,cnt=0,bt;
int cover[maxn],lef[maxn];

inline void prework(){
	sort(a+1,a+m+1);
	for(int i=1;i<=m;i++) if(a[i].r>rig){
		b[++cnt]=a[i];
		rig=a[i].r;
	}
	
	for(int i=1;i<=n;i++){
		ri=max(ri,p[i]);
		if(i<b[1].l||i>b[cnt].r) bt=max(bt,p[i]);
	}
} 

inline ll calc(int x){
	if(x<bt) return inf;
	ll ans=x*(ll)t;
	int now=0,pos=1;
	
	fill(cover+1,cover+n+1,0);
	for(int i=1;i<=n;i++) lef[i]=max(0,p[i]-x);
	
	for(int i=1;i<=n;i++){
		while(pos<cnt&&b[pos+1].l<=i) pos++;
		
		printf("%d %d
",i,b[pos].l);
		
		now+=cover[i];
        if(now<lef[i]){
        	int derta=lef[i]-now;
        	cover[b[pos].r+1]-=derta;
        	now+=derta;
        	ans+=(ll)derta;
		}
	}
	
	return ans;
}

inline void solve(){
	le=bt;
	while(le<=ri){
		mid=le+ri>>1;
		if(calc(mid)-calc(mid-1)<0) an=mid,le=mid+1;
		else ri=mid-1;
	}
	
	printf("%lld
",calc(an));
}

int main(){
	freopen("C.in","r",stdin);
	freopen("C.out","w",stdout);
	
	scanf("%d%d%d",&n,&m,&t);
	for(int i=1;i<=n;i++) scanf("%d",p+i);
	for(int i=1;i<=m;i++) scanf("%d%d",&a[i].l,&a[i].r);
	
	prework();
	solve();
	
	return 0;
}

  

原文地址:https://www.cnblogs.com/JYYHH/p/8509583.html