聰明的質監員(二分答案,前綴和)
阿新 • • 發佈:2018-08-16
驗證 前綴和 -m lis 2.7 不想 ret memset 包含
題目描述
小T
是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 nnn 個礦石,從 111 到 nnn 逐一編號,每個礦石都有自己的重量 wiw_iwi? 以及價值 viv_ivi? 。檢驗礦產的流程是:
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 。註意:不同區間可能重合或相互重疊。
輸出格式:
一個整數,表示所求的最小值。
思路:
暴力是不行的啦,而我又不會別的qwq
所以我就想辦法開始優化
一看w才1e6,好辦,可以二分答案了。
每次二分一個答案,然後驗證
如果二分出來的答案s-y小於0,那麽可能最優解在他的右邊
反之亦然
記得每次都要更新答案喲。
怎麽驗證呢?
我們就要用到前綴和
每次二分出一個數,我就按這個條件O(N)算出前綴和
詢問的回答就可以O(1)了
代碼:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define rii register int i #define rij register int j #define inf 19260817 #define int long long using namespacestd; long long qzhv[1000005],qzhw[1000005],s,n,m,v[200005],w[200005]; long long maxn,minx; long long z[200005],y[200005]; long long find(int wz) { long long ans=0; memset(qzhv,0,sizeof(qzhv)); memset(qzhw,0,sizeof(qzhw)); for(rii=1;i<=n;i++) { qzhv[i]=qzhv[i-1]; qzhw[i]=qzhw[i-1]; if(w[i]>=wz) { qzhw[i]++; qzhv[i]+=v[i]; } } for(rii=1;i<=m;i++) { long long ltt=z[i]; long long kkk=y[i]; ans+=(qzhw[kkk]-qzhw[ltt-1])*(qzhv[kkk]-qzhv[ltt-1]); } return ans; } long long qabs(long long val) { if(val>0) { return val; } else { return val*(-1); } } signed main() { maxn=-inf; minx=inf; scanf("%d%d%lld",&n,&m,&s); for(rii=1;i<=n;i++) { scanf("%d%d",&w[i],&v[i]); maxn=max(maxn,w[i]); minx=min(minx,w[i]); } for(rii=1;i<=m;i++) { scanf("%d%d",&z[i],&y[i]); } int l=minx-1; int r=maxn+2; minx=9999999999999999; while(l!=r) { if(r-l==1) { minx=min(minx,min(qabs(find(l)-s),qabs(find(r)-s))); break; } int mid=(l+r)/2; long long qaq=find(mid); minx=min(minx,qabs(qaq-s)); qaq=qaq-s; if(qaq<0) { r=mid; } else { l=mid; } } cout<<minx; }
聰明的質監員(二分答案,前綴和)