题解:P6958 [NEERC 2017] The Great Wall

题意已经够简洁了不再赘述。

思路:考虑暴力怎么做,就是枚举两个区间,没被覆盖的点价值为 \(a_i\),只被一个区间覆盖的点价值为 \(b_i\),被两个区间覆盖的点价值为 \(c_i\)。然后每种方案的价值存下来排序求出第 \(k\) 小的。

考虑优化。我们发现直接求出第 \(k\) 小的比较困难,但是求出比某个价值小的方案数量比较容易。考虑二分答案 \(x\)。

可以把方案分成两种:

  1. 两区间无交;
  2. 两区间有交。

对于无交的情况,我们枚举第一个区间,从之前遍历过的区间中选出与该区间无交的,便得到了一种方案。我们就是要求选择哪些区间和该区间组合价值小于 \(x\),数据结构维护即可。

对于有交的情况,同样枚举第一个区间,从之前遍历过的区间中选出与该区间有交的,便得到了一种方案。我们还是要求选择哪些区间和该区间组合价值小于 \(x\),双指针和数据结构维护即可。

我们还发现 \(a_i \lt b_i \lt c_i\),那么每个点一定会贡献 \(a_i\) 的价值,被至少一个区间覆盖会贡献 \(b_i-a_i\),被两个区间覆盖会贡献 \(c_i-b_i\)。令 \(c_i\gets c_i-b_i\)。

那么对于无交的情况,答案就是 \(\sum\limits_{i=1}^na_i+\sum\limits_{i=l_1}^{r_1}b_i+\sum\limits_{i=l_2,l_2\gt r_1}^{r_2}b_i\)。

对于有交的情况,答案就是 \(\sum\limits_{i=1}^{n}a_i+\sum\limits_{i=l_1}^{r_2}b_i+\sum\limits_{i=l_2}^{r_1}c_i\)。

二分的时候可以不管 \(\sum\limits_{i=1}^{n}a_i\),在最后把答案加上去就可以了。

感觉思路和代码都不是很困难。

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define endl '\n'
const int N=30005; 
#define ll long long
const ll V=30000000000;
int n,r;ll k;
int a[N],b[N],c[N];
ll sumb[N],sumc[N];
int num[N*36],ls[N*36],rs[N*36];
int rt,cnt;
void update(int &x,ll l,ll r,ll k,int v){
	if(!x)x=++cnt,num[x]=ls[x]=rs[x]=0;
	num[x]+=v;
	if(l==r)return;
	ll mid=l+r>>1;
	k<=mid?update(ls[x],l,mid,k,v):update(rs[x],mid+1,r,k,v);
	return;
}
int query(int x,ll l,ll r,ll k){
	if(!x)return 0;
	if(r<=k)return num[x];
	ll mid=l+r>>1;
	return k<=mid?query(ls[x],l,mid,k):num[ls[x]]+query(rs[x],mid+1,r,k);
}
inline bool check(ll x){
	ll res=0;rt=cnt=0;
	for(int i=n-r*2+1;i>=1;--i){
		update(rt,-V,V,sumb[i+r*2-1]-sumb[i+r-1],1);
		res+=query(rt,-V,V,x-(sumb[i+r-1]-sumb[i-1]));
	}
	rt=cnt=0;
	for(int i=n-r,L=n-r+2,R=n-r+1;i>=1;--i){
		while(L>i+1)
			--L,update(rt,-V,V,sumb[L+r-1]-sumc[L-1],1);
		while(R>i+r-1)
			update(rt,-V,V,sumb[R+r-1]-sumc[R-1],-1),--R;
		res+=query(rt,-V,V,x+sumb[i-1]-sumc[i+r-1]);
	}
	return res>=k;
}
inline void solve(){
	cin>>n>>r>>k;
	ll L=0,R=0,mid,sum=0;
	for(int i=1;i<=n;++i)cin>>a[i],sum+=a[i];
	for(int i=1;i<=n;++i)cin>>b[i];
	for(int i=1;i<=n;++i)cin>>c[i],R+=c[i];
	for(int i=1;i<=n;++i){
		c[i]-=b[i];b[i]-=a[i];
		sumc[i]=sumc[i-1]+c[i];
		sumb[i]=sumb[i-1]+b[i];
	}
	while(L<R){
		mid=L+R>>1;
		check(mid)?R=mid:L=mid+1;
	}
	cout<<sum+R<<endl;
	return;
}
int main(){
	IOS;
	int _=1;
	while(_--)solve();
	return 0;
}
/*
华风夏韵,洛水天依! 
*/

记录。

posted @ 2026-09-12 14:12  洛笺依韵  阅读(5)  评论(0)    收藏  举报