这题需要操作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;
}