P1314 聪明的质监员

题目大意

关于这题的思路

考时思路(关于W的取值)

很显然,当选定的W越大,则Y的值也会随之减少

那么这道题便是二分查找,于是便有了二分模板,但问题来了,我们要如何缩小范围呢?

我们先看张图

case1 Smid

由于我们需要将mid更靠近S,所以便是让mid变大,那么更新的端点也就可想而知了

case2 Smid

由于我们需要将mid靠近S,所以便是另一端点更新

注意

mid的值随W增大而减小

代码+一个小优化

优化理论,Ww[]

	while(r>=l){
		now=l+r>>1;
		k=check(W_[now]);
		ans=min(ans,abs_ull(k,S));
		if(k>S)l=now+1;
		else r=now-1;
	}

考时思路(获得W对应的值)

暴力,不说了,直接上代码

ull get(int l,int r,int W){
	int a=0,i;
	ull b=0;
	for(i=l;i<=r;i++)
	if(w[i]>=W){
		++a;
		b=b+v[i];
	}
	return a*b;
}
ull check(int W){
	int i,j;
	ull ans=0;
	for(i=1;i<=m;i++)
	ans+=get(ll[i],rr[i],W);
	return ans;
}

考后思路(暴力的优化)

上文的暴力程序时间复杂度为,显然会T,有没有可以优化的方法呢?

前缀和优化 O(2n

优化理论

对于每个l-r,我们需要求出中间的中间有多少个大于等于W的珠宝数量及其价值总和,总和就联想到前缀和

代码
for(i=1;i<=n;i++)
if(w[i]>=W){
	a[i]=a[i-1]+v[i];
	b[i]=b[i-1]+1;
}else{
	a[i]=a[i-1];
	b[i]=b[i-1];
}
for(i=1;i<=m;i++)
ans+=(a[rr[i]]-a[ll[i]-1])*(b[rr[i]]-b[ll[i]-1]);
代码解读

我们可以理解为将l-r中不符合要求的价值和数量均看做0,然后累加在ab

那么这一次的检阅结果就为 (a[右端点]-a[左端点-1])*(b[右端点]-b[左端点-1])的总和

关于这道题的总结

1.二分在具有单调性的查找中是一把利器,可以将时间复杂度从n降到㏒n

2.在一堆区间的优化方面,本质就是找出这些区间的共性,并减少共性方面的计算(也就是说只算一次),以本题为例,便是从小于W的珠宝每个区间都不符合的共性出手,进行前缀和优化

完整AC代码

#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
int n,m;
ull S;
int W_[200010],len;
int w[200010],v[200010];
int ll[200010],rr[200010];
int a[200010],b[200010];
ull abs_ull(ull a,ull b){
	if(a>b)return a-b;
	return b-a;
}
ull check(int W){
	ull ans=0;
	a[0]=b[0]=0;
	int i,j;
	for(i=1;i<=n;i++)
	if(w[i]>=W){
		a[i]=a[i-1]+v[i];
		b[i]=b[i-1]+1;
	}else{
		a[i]=a[i-1];
		b[i]=b[i-1];
	}
	for(i=1;i<=m;i++)
	ans+=(a[rr[i]]-a[ll[i]-1])*(b[rr[i]]-b[ll[i]-1]);
	return ans;
}
ull min(ull AA,ull BB){
	return AA>BB?BB:AA;
}
int main( ){
	std::ios::sync_with_stdio(false);
	cin>>n>>m>>S;
	int i,j;
	for(i=1;i<=n;i++){
		cin>>w[i]>>v[i];
		W_[++len]=w[i];
	}
	sort(W_+1,W_+len+1);
	ull ans=200000000000;
	for(i=1;i<=m;i++)
	cin>>ll[i]>>rr[i];
	int now;
	int l=1,r=len;
	ull k;
	while(r>=l){
		now=l+r>>1;
		k=check(W_[now]);
		ans=min(ans,abs_ull(k,S));
		if(k>S)l=now+1;
		else r=now-1;
	}
	cout<<ans<<endl;
}
posted @ 2019-12-14 11:35  Mikasa_Ackerman  阅读(105)  评论(0编辑  收藏  举报