NOIP2011 聰明的質檢員
阿新 • • 發佈:2019-02-10
這題需要操作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;
}