聰明的質檢員
【Problem description】
小 T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有n 個礦石,從1到n 逐一編號,每個礦石都有自己的重量wi 以及價值vi。檢驗礦產的流程是:
1、給定m 個區間[Li,Ri];
2、選出一個參數W;
3、對於一個區間[Li,Ri],計算礦石在這個區間上的檢驗值Yi :
這批礦的檢驗結果Y 為各個區間的檢驗值之和。即:
若這批礦產的檢驗結果與所給標準值S 相差太多,就需要再去檢驗另一批礦產。小T不想費時間去檢驗另一批礦產,所以他想通過調整參數W 的值,讓檢驗結果盡可能的靠近標準值S,即使得S-Y 的絕對值最小。請你幫忙求出這個最小值。
【Input format】
第一行包含三個整數 n,m,S,分別表示礦石的個數、區間的個數和標準值。
接下來的 n 行,每行2 個整數,中間用空格隔開,第i+1 行表示i 號礦石的重量wi 和價值vi 。
接下來的 m 行,表示區間,每行2 個整數,中間用空格隔開,第i+n+1 行表示區間[Li,Ri]的兩個端點Li 和Ri。註意:不同區間可能重合或相互重疊。
【Output format】
輸出只有一行,包含一個整數,表示所求的最小值。
【Algorithm design】
二分答案+前綴和
【Problem analysis】
求最接近標準的答案,很明顯二分答案。當然二分Y明顯很困難,應該分治W的值。這裏又可以引入離散化思想,因為W一定是一個礦的質量,不然不會有變化。確定W之後就是怎麽求Y的問題,首先要讀懂題目,其實意思很簡單,就是一個區間內可行礦的數量*可行礦的價值總和,因為區間老是重疊,如果循環處理,最大可能達到O(nm),所以想到前綴和處理,紀錄1到i的可行礦數量和價值總和(一開始還想到線段樹..),最後就是O((n+m)logn)的算法
【Source code】
#include <bits/stdc++.h>
#define MAXN 200010
#define ll long long
using namespace std;
int n,m;
ll s,ans,sumx[MAXN];
int w[MAXN],v[MAXN],b[MAXN],e[MAXN],sortw[MAXN],sum[MAXN];
void search(int l,int r)
{
if(l==r-1)
return ;
int mid=(l+r)>>1;
ll y=0;
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1];
sumx[i]=sumx[i-1];
if(w[i]>=sortw[mid])
sum[i]+=1,sumx[i]+=v[i];
}//在W確定後處理出前綴和
for(int i=1;i<=m;i++)
y+=(sum[e[i]]-sum[b[i]-1])*(sumx[e[i]]-sumx[b[i]-1]);//利用前綴和處理出Y
ans=min(ans,abs(y-s));//比較後紀錄最小差值
if(y<s)
search(l,mid);
else
search(mid,r);
}
int main()
{
freopen("qc.in","r",stdin);
freopen("qc.ans","w",stdout);
cin>>n>>m>>s;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i],sortw[i]=w[i];
for(int i=1;i<=m;i++)
cin>>b[i]>>e[i];
sort(sortw+1,sortw+n+1);//將礦值排序以便於二分
ans=1000000000001;
search(0,n+1);
cout<<ans<<endl;
return 0;
}
聰明的質檢員