[CSP-S模拟测试]:C(三分+贪心)

题目传送门(内部题46)


输入格式

第一行$3$个整数$n,m,t$。
第二行$n$个整数,表示$P_i$。
接下来$m$行每行两个整数,表示$L_i,R_i$。


输出格式

一行一个整数表示答案。


样例

样例输入:

3 3 2
6 2 5
1 1
2 2
3 3

样例输出:

11


数据范围与提示

样例解释:

最优方案为使用$2$次特殊加热器,$4$次$1$号加热器,$3$次$3$号加热器。

数据范围:

对于前$20\%$的数据:$t\geqslant n$
对于另$30\%$的数据:$P_i\leqslant 30$
对于所有数据:
$1\leqslant n,m,t\leqslant {10}^5$
$1\leqslant L_i,R_i\leqslant n$
$1\leqslant P_i\leqslant {10}^7$


题解

首先,如果你不傻,特殊加热器肯定是在一开始使用。

然而随着我们使用次数的增加,普通加热器所减少的费用也越来越小,所以这是一个上凸函数,所以我们考虑三分使用次数。

剩下的贪心即可。

时间复杂度:$\Theta(n\log_{1.5}(\max(P_i)))$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec{int L,R;}e[100001];
int n,m;
long long t;
int P[100001];
int cnt[100001];
long long ans=1LL<<60;
int h[100001],tag[100001];
long long judge(int x)
{
	for(int i=1;i<=n;i++)h[i]=max(0,P[i]-x);
	long long res=x*t;
	int flag=0;
	for(int i=1;i<=n;i++)
	{
		flag-=tag[i];
		tag[i]=0;
		h[i]=max(0,h[i]-flag);
		res+=h[i];
		flag+=h[i];
		tag[cnt[i]+1]+=h[i];
	}
	return res;
}
int main()
{
	scanf("%d%d%lld",&n,&m,&t);
	for(int i=1;i<=n;i++)cnt[i]=-1;
	for(int i=1;i<=n;i++)scanf("%d",&P[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&e[i].L,&e[i].R);
		cnt[e[i].L]=max(cnt[e[i].L],e[i].R);
	}
	int lft=0,rht=0;
	for(int i=1;i<=n;i++)
	{
		if(cnt[i-1]>=i)
		{
			rht=max(rht,cnt[i]);
			cnt[i]=rht;
		}
		if(cnt[i]==-1)lft=max(lft,P[i]);
	}
	rht=10000000;
	while(lft<=rht)
	{
		int mid=(lft+rht)>>1;
		long long flagl=judge(mid),flagr=judge(mid+1);
		if(flagl>=flagr)
		{
			lft=mid+1;
			ans=min(ans,flagr);
		}
		else
		{
			rht=mid-1;
			ans=min(ans,flagl);
		}
	}
	printf("%lld",ans);
	return 0;
}

rp++

posted @ 2019-09-19 17:40  HEOI-动动  阅读(208)  评论(0编辑  收藏  举报