说实话,这题其实应该放到普及+/提高
。思路其实就是简单的快速查找(其实你用二分或三分均可)+前缀和。
首先我们分析一下题面的式子,其实 Y i Y_i Yi 就等于该区间内大于等于 W W W 的数字数量与大于等于 W W W 的数字之和的乘积。也就是说,我们需要将重量排序后快速查找出一个与 S S S 的答案差距最小的地方。
那么,差距最小?肯定第一个想到的就是三分。所以我们的做法就出来了(下面附上思路):
sort();
三分枚举()
{
if(左中点的答案<右中点的答案) 左端点=左中点;
else 右端点=右中点;
}
那么我们就到了第二个问题:计算对于每一个 w i w_i wi 的答案。如果每一个区间都去枚举复杂度是 n 2 n^2 n2,肯定会 T L E TLE TLE 。所以我们就需要在 O ( n ) O(n) O(n) 的时间内处理出每一个 [ L i , R i ] [L_i,R_i] [Li,Ri] 的答案。
既然需要求的是每一个区间的答案,那么第二个算法就出来了:前缀和。我们只需要枚举每一位上的东西,然后用前缀和扫一遍统计即可。这样单次计算复杂度就降到了 O ( n ) O(n) O(n)。
最后如果三分没有枚举完,手动枚举剩下的那一点即可。
下面附上代码:
#include<iostream>
#include<algorithm>
#define N 1000005
#define abs(a) ((a)<0?-(a):(a))
#define INF 9999999999999999ll
using namespace std;
long long ans=INF,n,m,S,w[N],v[N],l[N],r[N],sum[N],sum2[N],q[N];
long long check(int W)
{
long long now=0;
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1],sum2[i]=sum2[i-1];
if(w[i]>=W) sum[i]+=v[i],sum2[i]++;
}
for(int i=1;i<=m;i++)
now+=(long long)(sum2[r[i]]-sum2[l[i]-1])*(sum[r[i]]-sum[l[i]-1]);
return abs(now-S);
}
int main()
{
cin>>n>>m>>S;q[0]=-1;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i],q[i]=w[i];
for(int i=1;i<=m;i++)
cin>>l[i]>>r[i];
sort(q+1,q+n+1);
int l=1,r=n;
while(r>=l+4)
{
int mid=(l+r)/2,mid2=(r+mid)/2;
if(check(q[mid])>check(q[mid2])) l=mid;
else r=mid2;
}
for(int i=l;i<=r;i++)
if(check(q[i])<ans)
ans=check(q[i]);
cout<<ans;
return 0;
}