当前位置: 首页 > 工具软件 > Frets On Fire > 使用案例 >

D. Frets On Fire(思维,排序)

百里秋月
2023-12-01

先 按 照 s i 从 小 到 大 排 序 先按照s_i从小到大排序 si

设 第 一 个 区 间 是 [ s 1 + l , s 1 + r ] , 那 么 对 答 案 贡 献 是 r − l + 1 设第一个区间是[s_1+l,s_1+r],那么对答案贡献是r-l+1 [s1+l,s1+r],rl+1

考 虑 第 二 个 区 间 [ s 2 + l , s 2 + r ] , 那 么 对 答 案 是 多 少 ? 考虑第二个区间[s_2+l,s_2+r],那么对答案是多少? [s2+l,s2+r],?

显 然 当 s 2 + l > s 1 + r 时 , 对 答 案 贡 献 是 r − l + 1 , 也 就 是 和 上 一 个 区 间 不 重 叠 显然当s_2+l>s1+r时,对答案贡献是r-l+1,也就是和上一个区间不重叠 s2+l>s1+r,rl+1,

否 则 重 叠 , 对 答 案 贡 献 是 s 2 − s 1 否则重叠,对答案贡献是s2-s1 ,s2s1

所 以 每 个 区 间 对 答 案 的 贡 献 是 m i n ( r − l + 1 , s i − s i − 1 ) 所以每个区间对答案的贡献是min(r-l+1,s_i-s_{i-1}) min(rl+1,sisi1)

但 是 , 这 样 的 复 杂 度 还 不 够 优 秀 , 每 次 询 问 都 要 O ( n ) 的 时 间 来 求 \color{Red}但是,这样的复杂度还不够优秀,每次询问都要O(n)的时间来求 ,,O(n)

所 以 我 们 把 所 有 的 s i − s i − 1 求 出 来 , 记 作 数 组 d [ i ] 所以我们把所有的s_i-s_{i-1}求出来,记作数组d[i] sisi1,d[i]

那 么 显 然 , 如 果 d i > = r − l + 1 , 那 么 贡 献 r − l + 1 可 以 取 满 那么显然,如果d_i>=r-l+1,那么贡献r-l+1可以取满 ,di>=rl+1,rl+1

否 则 , 区 间 有 部 分 重 复 了 , 贡 献 为 d i 否则,区间有部分重复了,贡献为d_i ,,di

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e5+10;
ll s[maxn],d[maxn],pre[maxn],n;
ll find(ll x)
{
	ll l=1,r=n,mid;
	while(r>l)
	{
		mid=l+r>>1;
		if(d[mid]>=x)	r=mid;
		else	l=mid+1;
	}
	return r;
}
int main()
{
	cin >> n;
	for(int i=1; i<=n ;i++)	cin >> s[i];
	sort(s+1,s+1+n);
	for(int i=2;i<=n;i++)	d[i-1]=s[i]-s[i-1];
	sort(d+1,d+n);
	cout<<endl;
	for(int i=1;i<n;i++)	pre[i]=pre[i-1]+d[i];
	int q;cin>>q;
	for(int i=1;i<=q;i++)
	{
		ll l,r;
		cin>>l>>r;
		ll len=r-l+1;
		ll num=find(len);//num后的可以打满,之前的要减去d[i]
		ll ans=len*(n-num+1)+pre[num-1];
		cout<<ans<<" "; 
	}
}
 类似资料: