D. Frets On Fire

题意:输入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;
}

猜你喜欢

转载自blog.csdn.net/chenyume/article/details/89065578