[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++