COGS 631. [NOIP2011] 聪明的质监员 解题报告

来源:互联网 发布:java数组增删改查 编辑:程序博客网 时间:2024/05/19 08:01

631. [NOIP2011] 聪明的质监员

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

Yi=j1×jvj, j[Li,Ri] wjW,j

这批矿产的检验结果Y为各个区间的检验值之和。即:

Y=i=1mYi

若这批矿产的检验结果与所给标准值 S 相差太多,就需要再去检验另一批矿产。小 T 不想费时间去检验另一批矿产,所以他想通过调整参数 W 的值,让检验结果尽可能的靠近标准值 S,即使得SY的绝对值最小。请你帮忙求出这个最小值。 


二分答案+精妙前缀和+维护序号=AC

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<cmath>using namespace std;typedef long long ll; const int maxn=2001000;ll n,m,s;//sum存前缀和,sum[i]-sum[j]表示[i,j)的和//cnt存序号,cnt[i]表示i位置矿石的序号,便于公式一运算,如果不用到该位置矿石就让下一个"继承"ll w[maxn],d[maxn],l[maxn],r[maxn],sum[maxn],cnt[maxn];ll max_w=-1; void Init(){cin>>n>>m>>s;for(int i=1;i<=n;i++){cin>>w[i]>>d[i];max_w=max(max_w,w[i]);//记录最大值,为答案区间右端点 }for(int i=1;i<=m;i++)cin>>l[i]>>r[i];}ll Calc(ll W){for(ll i=1;i<=n;i++){sum[i]=sum[i-1]; cnt[i]=cnt[i-1];//"继承"上一个前缀和及前缀序号if(w[i]>=W){sum[i]+=d[i]; cnt[i]++;}//有符合条件的矿石就更新}ll ans=0;for(ll i=1;i<=m;i++) ans+=(cnt[r[i]]-cnt[l[i]-1])*(sum[r[i]]-sum[l[i]-1]);//左闭右闭区间,故为l[i]-1return ans;}void Work(){ll L=0,R=max_w+1;while(L<R-1){ll M=L+((R-L)/2);if(Calc(M)>s)L=M;else R=M;}cout<<(min(abs(Calc(L)-s),abs(Calc(R)-s)));//二分结果不一定最小,比较一番 }int main(){ios::sync_with_stdio(false);#ifndef DEBUGstring FileName="qc";freopen((FileName+".in").c_str(),"r",stdin);freopen((FileName+".out").c_str(),"w",stdout);#endifInit();Work();}

0 0