题目传送门
题意解析:题目告诉了你n块矿石,并且每次选取一段矿石,给出了矿石的质量计算方式,让你求出每段质量之和(Y)与要求的质量和(S)的最小差(即求min{abs(Y-S)}),每次计算的方案是,对于一个选定的重量W,每段的质量=重量超过W的个数*重量超过W的价值之和。
My opinion:我一开始看到这题目,有一件事是很明显的,如果我们知道了W,那么我们可以快速计算出每一段的质量,(不过像我这种zz一开始认为需要用数据结构维护,比如线段树,但是后来突然发现自己犯蠢,其实O(n)线扫维护片段和就好了)。我们不能枚举W,这样显然要超时,那么我们自然想到了二分,但是因为这题答案是要求abs(Y-S),是一个山峰,所以我就去写了三分,于是完美爆炸(后来有dalao给我证明了二分也是可行了,而然我这种蒟蒻没发现),三分会有一些问题存在。
如图:
就是这样,所以该怎么三分呢,我们可以不要去三分值,因为最优解的W一定存在于一种W=w[i](任意i)。所以我们可以去以w[i]去三分,这样就可以了。
总结:
1、输入。
2、三分(二分),记得三分的数组要排序+去重。
3、计算每种质量。
4、输出。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=a;i>=n;i--)
#define Clear(a,x) memset(a,x,sizeof(a))
#define ll long long
#define INF 2000000000
#define eps 1e-8
using namespace std;
ll read(){
ll x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9') f=ch=='-'?-1:f,ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int maxn=200005;
int n,m;
ll S;
int w[maxn],v[maxn],a[maxn],l[maxn],r[maxn];
int L,R;
ll sum[maxn],c[maxn];
ll calc(int W){
c[0]=0,sum[0]=0;
rep(i,1,n){
sum[i]=sum[i-1];
c[i]=c[i-1];
if (w[i]>=W) sum[i]+=(ll)v[i],c[i]++;
}
ll Y=0;
rep(i,1,m)
Y+=(sum[r[i]]-sum[l[i]-1])*(c[r[i]]-c[l[i]-1]);
return Y;
}
int main(){
n=read(),m=read(),S=read();
rep(i,1,n) w[i]=read(),v[i]=read(),a[i]=w[i];
rep(i,1,m) l[i]=read(),r[i]=read();
sort(a+1,a+n+1);
R=unique(a+1,a+n+1)-a-1;
L=0;
ll ans=-1;
while (L<=R){
int mid1=(L+R)>>1,mid2=min(R,mid1+1);
ll y1=calc(a[mid1]),y2=calc(a[mid2]);
y1=abs(y1-S),y2=abs(y2-S);
if (y1<y2) R=mid2-1;
else if (y1>y2) L=mid1+1;
else {
ans=y1;
L=mid1+1;
R=mid2-1;
}
}
printf("%lld\n",ans);
return 0;
}