Code[vs] 1138— NOIP2011 聰明的質監員 (二分答案+字首和)
阿新 • • 發佈:2019-01-24
題意:
有n個礦石,每個礦石有重量和價值兩個引數,質檢員可以調節一個最低重量w,每個區間的檢查值為該區間內重量>=w的礦石價值和 *(重量>=w的個數),求m個區間的檢查值之和 與 標準值s之差的絕對值的最小值
題解:
檢查值之和 只和 w 有關,不難發現,若檢查值之和 > s,則小於w的一定會使絕對值更大,檢查值之和 < s,大於w的也一定會使絕對值更大,所以 可以二分 w ,計算檢查值之和時用字首和
程式碼:
/********************* 作者:Amazing_ 題目:p1138 聰明的質監員 時間:2018.11.13 *********************** */ #include <cmath> #include <cstdio> #include <string.h> #include <iostream> using namespace std; const int MAXN = 2e5+55; typedef long long ll; struct ore { int w; int v; }; ore a[MAXN]; // 礦石 ore b[MAXN]; // 檢查的區間 ll c[MAXN]; // 滿足條件的個數字首和 ll d[MAXN]; // 滿足條件的價值字首和 ll n,m,s,ans; bool check(int mid) { memset(c,0,sizeof(c)); memset(d,0,sizeof(d)); for(int i=1; i<=n; ++i) { if(a[i].w>=mid) { c[i] = c[i-1] + 1; d[i] = d[i-1]+a[i].v; } else { c[i] = c[i-1]; d[i] = d[i-1]; } } ll val = 0; for(int i=1; i<=m; ++i) { ll num = c[b[i].v] - c[b[i].w-1]; ll tep = d[b[i].v] - d[b[i].w-1]; val += 1ll*num*tep; } ans = min(ans,(ll)abs((long double)val-s)); return val>s? true : false; } int main() { cin>>n>>m>>s; for(int i=1; i<=n; ++i) scanf("%d %d",&a[i].w,&a[i].v); for(int i=1; i<=m; ++i) scanf("%d %d",&b[i].w,&b[i].v); int l = 1; int r = (int)1e6+6; ans = 1ll<<60; while(l<=r) { int mid = (l+r)>>1; if(check(mid)) { l = mid+1; } else r = mid-1; } cout<<ans; return 0; }