杂题训练之三
https://www.luogu.org/problem/P1314
这题目很明显地告诉我们是二分
如果你看不出来的话:二分的判断。
可以看到:在W取0时,所有的区间内的矿石都可以选上,
而在W大于最大的质量时,所有的矿石都选不上。
然后简单算一下就发现:
W越大,矿石选的越少,W越小,矿石选的越多。
所以,随着W增大,Y值减小;
所以:二分的判断条件出来了:
当Y>s 时,需要增大WW来减小Y,从而|Y-s|变小;
当Ys时,|Y-s|0;
当Y<s时,需要减小W来增大Y,从而|Y-s|变大;
再就是前缀和(因为题目要求是区间和)
因为二分是logn,维护前缀和是On的,所以nlogn是稳稳的过
code by std(码风比较丑,不是我写的,我找了个最清晰的):
#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
const int N=1001000;
template<class T>
void read(T &x)
{
x=0;
T f;
f=1;
char c;
c=getchar();
while((c<'0'||c>'9')&&c!='-')
{
c=getchar();
}
if(c=='-')
{
f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
x=x*f;
}
int w[N],v[N],L[N],R[N],n,m;
ll s,sum[N],num[N];
ll check(ll W)
{
int i;
ll ans;
ans=0;
for(i=1;i<=n;i++)
{
if(w[i]<W)
{
num[i]=num[i-1];
sum[i]=sum[i-1];
}
else
{
sum[i]=sum[i-1]+v[i];
num[i]=num[i-1]+1;
}
}
for(i=1;i<=m;i++)
{
ans+=(sum[R[i]]-sum[L[i]-1])*(num[R[i]]-num[L[i]-1]);
}
return ans;
}
inline ll labs(ll a)
{
return a>0 ? a : -a;
}
int main()
{
int i;
ll l,r,mid,ans;
read(n);
read(m);
read(s);
for(i=1;i<=n;i++)
{
read(w[i]);
read(v[i]);
}
for(i=1;i<=m;i++)
{
read(L[i]);
read(R[i]);
}
l=0;
r=1e6;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid)>s)
{
l=mid+1;
}
else
{
r=mid-1;
}
}
ans=labs(check(l)-s);
if(ans>labs(check(r)-s))
{
ans=labs(check(r)-s);
}
printf("%lld",ans);
}