『NOIP 2011』聰明的質監員(二分答案 + 前綴和)
阿新 • • 發佈:2018-09-21
可能 can 表示 代碼 標準 和數 怎麽 abs load 的值,讓檢驗結果盡可能的靠近標準值\(S\),即使得\(S-Y\)的絕對值最小。請你幫忙求出這個最小值。
,這樣我們的總復雜度就是\(O(log_{玄學} \times (m+n))\)。
題目鏈接
題目描述
小T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 \(n\) 個礦石,從 \(1\) 到 \(n\) 逐一編號,每個礦石都有自己的重量 \(w_i\) 以及價值 \(v_i\) 。檢驗礦產的流程是:
1 、給定m個區間\([L_i,R_i]\);
2 、選出一個參數\(W\);
3 、對於一個區間\([L_i,R_i]\),計算礦石在這個區間上的檢驗值\(Y_i\):
這批礦產的檢驗結果 \(Y\) 為各個區間的檢驗值之和。即:\(Y_1+Y_2...+Y_m\)
若這批礦產的檢驗結果與所給標準值 \(S\) 相差太多,就需要再去檢驗另一批礦產。小T不想費時間去檢驗另一批礦產,所以他想通過調整參數 \(W\)
解題思路
題目要求參數 \(W\) 最小,我們可以考慮二分 \(W\) 的值。
二分後我們就要考慮怎麽檢驗是否合法了。
因為 \(m\) 的最大值是 \(2e5\) 所以我們不可能暴力枚舉每個區間。每個區間必須在 \(O(1)\) 的時間內處理出來。我們發現這個式子的含義就是區間內 \(w\) 大於等於 \(W\) 的礦石的個數稱這些礦石的價值之和。
顯然,我們可以在\(O(n)\)的復雜度內處理出來兩個前綴和數組,一個表示個數,一個表示和。每個區間的查詢的復雜度就被降低到了\(O(1)\)
腦子抽了,一開始還想著三分。。。。
代碼
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define ll long long using namespace std; const int maxn=2000050; ll n,m,s; ll w[maxn],v[maxn],pre[maxn],prew[maxn]; int l[maxn],r[maxn]; inline ll check(ll x){ for(register int i=1;i<=n;i++){ pre[i]=prew[i]=0; if(w[i]>=x){ pre[i]=1; prew[i]=v[i]; } pre[i]+=pre[i-1]; prew[i]+=prew[i-1]; } ll ans=0; for(register int i=1;i<=m;i++){ ll sum=pre[r[i]]-pre[l[i]-1]; ll tot=prew[r[i]]-prew[l[i]-1]; //cout<<pre[r[i]]<<' '<<pre[l[i]-1]<<' '<<prew[r[i]]<<' '<<prew[l[i]-1]<<endl; ans+=sum*tot; } return ans; } int main(){ scanf("%lld%lld%lld",&n,&m,&s); ll ma=0; for(register int i=1;i<=n;i++){ scanf("%lld%lld",&w[i],&v[i]); ma=max(ma,w[i]); } for(register int i=1;i<=m;i++){ scanf("%d%d",&l[i],&r[i]); } register ll l=0,r=999999999999999,ans=999999999999999; while(l<=r){ register ll mid=(l+r)>>1; ll tmp=check(mid); //cout<<l<<' '<<r<<' '<<tmp<<endl; if(tmp>s){ l=mid+1; } else r=mid-1; ans=min(ans,abs(tmp-s)); } cout<<ans<<endl; }
『NOIP 2011』聰明的質監員(二分答案 + 前綴和)