[luogu-1314]noip2011 day2-T2聪明的质监员 题解

来源:互联网 发布:淘宝查看足迹 编辑:程序博客网 时间:2024/05/17 03:19

题目传送门
题意解析:题目告诉了你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-8using 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;}
原创粉丝点击