题解:CF1119D Frets On Fire

大水题。

首先,若区间内只有一根弦,不会对答案有贡献。

我们思考如何对答案产生贡献。我们知道,对于每一个 \(s_i\),都会产生一段 \(s_i+r-l\) 的连续序列,在对 \(s\) 数组排序后,若每个 \(s_i+r-l \ge s_{i+1}\) 则答案为 \(s_n+r-(s_1+l)+1\)

若够不到下一位呢?我们在 \(s_n+r-(s_1+l)+1\),上减去够不到的位数即可。

过程的实现:我们先对 \(s\) 数组排序,将 \(s_{i+1}-s_i \ge 2\)\(s_{i+1}-s_i\) 压入 \(c\) 数组中,对 \(c\) 排序,并且求其前缀和数组 \(sum\)。设 \(c\) 数组长度为 \(m\)

对于询问 \(l,r\),若 \(r-l\) 大于 \(c_m\),二分查找 \(c\) 数组中第一个大于 \(r-l\) 的数,\(c_x\)。够不到的位数即为 sum[m]-sum[x-1]-ch*(m-x+1)。用 \(s_n+r-(s_1+l)+1\) 减去即可。反之,输出 \(s_n+r-(s_1+l)+1\) 即可。

代码(讲的很详细了不放注释了):

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+7;
int a[N],sum[N],c[N];
int n,m;
signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n);
	for(int i=2;i<=n;i++) {
		if(a[i]-a[i-1]>1) c[++m]=a[i]-a[i-1]-1;
	}
	sort(c+1,c+1+m);
	for(int i=1;i<=m;i++) sum[i]=sum[i-1]+c[i];
	int qu;
	cin>>qu;
	while(qu--){
		int l,r;
		cin>>l>>r;
		int ch=r-l,ans=a[n]+r-a[1]-l+1;
		if(ch<c[m]) {
			int w=upper_bound(c+1,c+1+m,ch)-c;
//			cout<<w<<'\n';
			int su=sum[m]-sum[w-1]-ch*(m-w+1);
			ans-=su;
		}
		cout<<ans<<' ';
	}
	return 0;
}
posted @ 2024-05-26 11:44  shoot_down  阅读(4)  评论(0编辑  收藏  举报