题意:输入n个数字,代表n个序列,每个序列的首位为s[i],接下来输入询问次数,每次询问[l,r]区间内所有序列总共不同的数字的个数。
思路:先排序去重,再对相邻元素差分,对差分数组排序,计算差分数组的前缀和。
图示:
那么我们可以对差分数组进行排序,二分去找第一个大于r-l+1的数,假设这个位置为k,那么这个位置之后的其差分值都大于r-l+1,故k以及k之后的贡献度都为r-l+1,而k之前的则为差分数组的和。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const int maxn=1e5+7;
ll a[maxn];
ll b[maxn];//差分数组;
ll sum[maxn];//前缀和;
void init(){
sort(a+1,a+n+1);
m=unique(a+1,a+1+n)-a-1;//去重后元素的个数;
for(int i=1;i<m;++i)
b[i]=a[i+1]-a[i];
sort(b+1,b+m);
for(int i=1;i<m;++i)
sum[i]=sum[i-1]+b[i];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%llu",&a[i]);
init();
int q;
scanf("%d",&q);
ll l,r;
while(q--){
scanf("%I64d%I64d",&l,&r);
ll res=r-l+1;//最前面的数他的[l,r]的数都是可选的;
ll xl=1,xr=m-1,mid;
while(xl<=xr){//二分找第一个大于r-l+1的位置;
mid=(xl+xr)>>1;
if(b[mid]<=r-l+1) xl=mid+1;
else xr=mid-1;
}
--xl;
res+=sum[xl]+(m-1-xl-1+1)*(r-l+1);
printf("%I64d%s",res,(q?" ":"\n"));
}
return 0;
}