NOIP2011 聪明的质监员 洛谷P1314
题目描述:
小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1到n 逐一编号,每个矿石都有自己的重量 wi 以及价值vi 。检验矿产的流程是:
1 、给定m 个区间[Li,Ri];
2 、选出一个参数 W;
3 、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi:

这批矿产的检验结果Y 为各个区间的检验值之和。即:Y1+Y2...+Ym
若这批矿产的检验结果与所给标准值S 相差太多,就需要再去检验另一批矿产。小T
不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近
标准值S,即使得S-Y 的绝对值最小。请你帮忙求出这个最小值。
输入格式:
第一行包含三个整数n,m,S,分别表示矿石的个数、区间的个数和标准值。
接下来的n 行,每行2个整数,中间用空格隔开,第i+1 行表示 i 号矿石的重量 wi 和价值vi。
接下来的m 行,表示区间,每行2 个整数,中间用空格隔开,第i+n+1 行表示区间[Li,Ri]的两个端点Li 和Ri。注意:不同区间可能重合或相互重叠。
输出格式:
输出文件名为qc.out。
输出只有一行,包含一个整数,表示所求的最小值。
最大值要开到1e18啊!!!!!!
调了一辈子,还是代码经验太少了。
题解:
这题可以用二分做。
l,r分别为w的最小值-1和w的最大值+1,
对于每个mid,我们扫一遍矿石,利用前缀和思想,计算出到i为止,w大于mid的矿石的个数和v的和。
再扫一遍每个区间,求出当前检验值,如果检验值>s的话l=mid+1,否则r=mid-1
如果s=检验值的话直接break
代码:
//by lzx #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> using namespace std; const int N=200010; #define inf 1e18 #define ll long long ll n,m; ll s; struct node{ ll w,v; }sa[N]; struct node1{ ll l,r; }qu[N]; ll l=inf,r=-inf; ll ans=inf; ll sumv[N],sum[N]; ll check(ll x) { // memset(sum,0,sizeof(sum)); //memset(sumv,0,sizeof(sumv)); sum[0]=0; sumv[0]=0; //printf("!%lld\n",x); for(ll i=1;i<=n;i++) { if(sa[i].w>=x) sum[i]=sum[i-1]+1,sumv[i]=sumv[i-1]+sa[i].v; else sum[i]=sum[i-1],sumv[i]=sumv[i-1]; } // printf("\n"); ll yu=0; for(ll i=1;i<=m;i++) { yu+=(sum[qu[i].r]-sum[qu[i].l-1])*(sumv[qu[i].r]-sumv[qu[i].l-1]); // printf("%lld %lld\n",sumv[qu[i].r],sumv[qu[i].l-1]); } ans=min(abs(yu-s),ans); //prllf("%lld\n",ans); return yu; } int main() { scanf("%lld%lld%lld",&n,&m,&s); for(ll i=1;i<=n;i++) { scanf("%lld%lld",&sa[i].w,&sa[i].v); l=min(sa[i].w,l);r=max(sa[i].w,r); } for(ll i=1;i<=m;i++) { scanf("%lld%lld",&qu[i].l,&qu[i].r); if(qu[i].l>qu[i].r) swap(qu[i].l,qu[i].r); } ll mid; l--; r++; while(l<=r) { mid=(l+r)>>1; ll yu=check(mid); //printf("%lld %lld %lld %lld\n",l,r,mid,yu); if(yu>s)l=mid+1; else r=mid-1; if(yu==s) {ans=0;break;} } printf("%lld",ans); }

浙公网安备 33010602011771号