1. 程式人生 > >NOIP2011 聰明的質檢員

NOIP2011 聰明的質檢員

這題需要操作w,使得y與s儘量接近
那麼考慮到y可以比s小一點也可以大一點,可以分開考慮,若y < s 則嘗試把y弄大一點,
這樣就可以二分w,使y越來越接近s,並且實時記錄最優解,也不用管最後w是多少
難點在如何快速算檢驗值
考慮多個區間疊加浪費了時間,有兩種可能優化:
1.把重疊區間預先求出來…太複雜,寫了估計也不對
2.其實就是對區間一些滿足要求的點求和,那麼不考慮不滿足的點,區間求和,或者是說各種求和,都可以用字首和變成O(1)來做(類似還有樹上字首和 f[i]表示從根到i的和)

如果說沒想到字首和,是因為沒能把當前問題抽象提升出來

#include <algorithm>
#include <iostream> #include <cstring> #include <cstdio> using namespace std; #define debug(x) cerr << #x << "=" << x << endl; const int MAXN = 200000 + 10; int n,m,tot,wmax,w[MAXN],v[MAXN]; typedef long long ll; ll s, ans; int fs1[MAXN]; ll fs2[MAXN]; struct sect{ int
l,r; }sec[MAXN]; ll ab(ll x) { if(x < 0) return -x; return x; } ll calc(int wi) { ll y = 0; memset(fs1, 0, sizeof(fs1)); memset(fs2, 0, sizeof(fs2)); for(int i=1; i<=n; i++) { if(w[i] >= wi) { fs1[i] = 1; fs2[i] = v[i]; } fs1[i] += fs1[i-1
]; fs2[i] += fs2[i-1]; } for(int i=1; i<=m; i++) { int l = sec[i].l, r = sec[i].r; int nj = fs1[r] - fs1[l-1]; ll ns = fs2[r] - fs2[l-1]; y += ns * nj; } return y; } int main() { cin >> n >> m >> s; for(int i=1; i<=n; i++) { scanf("%d %d", &w[i], &v[i]); wmax = max(wmax, w[i]); } for(int i=1; i<=m; i++) { scanf("%d %d", &sec[i].l, &sec[i].r); } int l = 0, r = wmax; ans = ab(s - calc(wmax));//這個地方超易錯 注意答案是s-y不是y 我第一回寫成了ans = y 所以靜態差錯很重要! while(l <= r) { int mid = l+r>>1; ll y = calc(mid); ll now = ab(s-y); if(now < ans) { ans = now; } if(y > s) { l = mid+1; } else { r = mid-1; } } cout << ans; return 0; }