P1314 聪明的质监员

https://www.luogu.org/problemnew/show/P1314

原题题意带来的坑:

1.流程三的公式是可以写成这样的:\sum_{i=l}^{r,w[i]>=W}1\times \sum_{j=l}^{r,w[j]>=W}V^{}_{j}

换句话说:区间[l,r]Y的值=区间内满足W{}_{i}\geq W的矿石的总个数\times区间内满足W{}_{i}\geq W的矿石的价值之和

2.题干说让你输出的是在最优情况下的YS-Y绝对值

明确了这两个地方,我们开始分析它二分的单调性在哪里:

单调性:当你选取的检验参数W越大,满足W{}_{i}\geq W的矿石显然越少,区间的检验值Y就越低。

              反之,当你选取的检验参数W越小,满足W{}_{i}\geq W的矿石显然会越多,区间的检验值Y就越大。

那么我们二分检验值W,初始化二分端点l=0,r=max(W{}_{i}),最开始时,对于任何区间,W=rmax(W{}_{i}))的Y值最大,W=l(0)时Y值最小。

二分流程:

1.取变量mid=\frac{l+r}{2}

2.检验W=mid时所有区间的Y值之和:

(1)若此时\sum_{i=1}^{m}Y{}_{i}\geqslant S,根据Y取值的单调性可得:W=l下的Y值之和肯定\geqW=mid时的Y值之和,又由于W=mid\sum_{i=1}^{m}Y{}_{i}\geqslant S,所以W=l时的Y-S必然大于W=mid时的Y-S,所以W\in [l,mid)时的答案不可能比W=mid时的答案更优,故取l=mid

(2)若此时\sum_{i=1}^{m}Y{}_{i}< S,同(1)理,W\in (mid,r]时的答案不可能比W=mid时的答案更优,故取r=mid

check函数的复杂度是O(n+m)的(具体维护方法见代码),所以总的时间复杂度为O( (n+m)log (max(W{}_{i})) )

Code:

#include<cstdio>
#include<cstring>
#include<iostream>
#define ri register int
using namespace std;

const int MAXN=200020;
int n,m,w[MAXN],lft[MAXN],rit[MAXN],l,r,mid;
long long s,v[MAXN],sum[MAXN],hf[MAXN],now,ans;
//原题数据范围较大,需开long long 

long long ins(long long x)
{
	if(x<0)	 return (-1)*x;
	return x;
}

long long mincmp(long long x,long long y)
{
	if(x<=y)	return x;
	return y;
}

long long check(int minn)//二分W值 
{
	memset(sum,0,sizeof(sum));
	memset(hf,0,sizeof(hf));
	long long tot=0;
	for(ri i=1;i<=n;i++)
	{
		sum[i]=sum[i-1],hf[i]=hf[i-1];
		if(w[i]>=minn)	sum[i]+=v[i],hf[i]+=1;
		//hf[i]:维护区间 [1,i]内合法的矿石的个数
		//sum[i]:维护区间 [1,i]内合法的矿石的价值之和。 
	}
	for(ri i=1;i<=m;i++)	tot+=(sum[rit[i]]-sum[lft[i]-1])*(hf[rit[i]]-hf[lft[i]-1]);
	//利用上面O(n)维护的前缀和O(m)快速求求sigma Yi 
	return tot;
}

int main()
{
	scanf("%d%d%lld",&n,&m,&s);
	for(ri i=1;i<=n;i++)
	{
		scanf("%d%lld",&w[i],&v[i]);
		r=max(r,w[i]);
	}
	for(ri i=1;i<=m;i++)	scanf("%d%d",&lft[i],&rit[i]);
	while(l+1<r)
	{
		mid=(l+r)>>1;
		now=check(mid);
		if(now<s)	r=mid;
		if(now>s)	l=mid;
	}
	now=check(l),ans=check(r);
	if(ins(now-s)<=ins(ans-s))	{ cout<<ins(now-s); return 0; }//输出的时|S-Y|而非S-Y 
	cout<<ins(ans-s);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值