https://www.luogu.org/problem/show?pid=1314#sub
看到这个题,想到二分是不难的,但是如果就直接上二分,简单暴力地求和,就会超时。O(mnlogw)
观察题目,区间可能是重合的或者叠盖,就考虑到前缀和来优化,将m*n缩减到m+n。
最终复杂度为O((m+n)*logw).
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#define LL long long
using namespace std;
int n,m;
LL s;
int w[200005],v[200005];
int L[200005],R[200005];
LL h[200005],num[200005];
int l=10000000,r;
LL check(int x)
{
LL y=0,t=0,yy=0;
/*for(int i=1;i<=m;i++)//O(m*n*logw) TLE
{
y=0,t=0;
for(int j=L[i];j<=R[i];j++)
if(w[j]>=x) y+=v[j],t++;
yy+=t*y;
}*/
//memset(h,0,sizeof(h));
//memset(num,0,sizeof(num));
for(int i=1;i<=n;i++)//O((m+n)*log w)
{
h[i]=h[i-1];num[i]=num[i-1];
if(w[i]>=x) h[i]+=v[i],num[i]++;
}
for(int i=1;i<=m;i++)
yy+=(h[R[i]]-h[L[i]-1])*(num[R[i]]-num[L[i]-1]);
return yy;
}
int main()
{
//freopen("qc.in","r",stdin);
//freopen("qc.out","w",stdout);
scanf("%d%d%lld",&n,&m,&s);
for(int i=1;i<=n;i++)
scanf("%d%d",&w[i],&v[i]),r=max(r,w[i]),l=min(l,w[i]);
for(int i=1;i<=m;i++)
scanf("%d%d",&L[i],&R[i]);
int mid;
LL ans=200000000000;
while(l<=r)
{
mid=(l+r)>>1;
LL Y=check(mid);
if(Y<s)
r=mid-1;
if(Y>s)
l=mid+1;
if(Y==s) {printf("0
");return 0;}
ans=min(ans,abs(s-Y));
}
printf("%lld
",ans);
fclose(stdin);fclose(stdout);
return 0;
}
/*
5 3 11
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
6
*/
/*
5 1 5
1 5
2 5
3 5
4 5
5 5
1 3
0*/
/*
5 2 11
1 3
2 3
3 3
4 3
5 2
1 5
2 3
1*/