题目链接:http://codeforces.com/contest/1119/problem/D
题意:给n(<=1e5)个数s[i],i=1..n,(0<=s[i]<=1e18)分别表示每一行的起始值,每个组有1e18+1列,第i行第j(0<=j<=1e18)列的值为s[i]+j,有q组询问(q<=1e5),每组询问给出两个值l,r,问每一行的第 l 列到第 r 列有多少个不同的值。
思路:题意很简单,但看到数据量,q<=1e5,就要想到能不能预处理之类的。显然问题在于每一行可能出现数据交叉的情况,先将s数组按升序排列,每一行最多有w=r-l+1个不同的数,不访从最后一行开始,即第n行有w个不同的数,则第i(1<=i<n)行有min(si+1-si,w)个数(与前面已经出现的数不同),显然这里的si+1-si是可以提前算出来的,那么我们用di=di+1-di,然后将d[i]按升序排序,sum[i]表示前i个d[i]之和。这样在询问的时候输入得到w,然后只需二分查找到第一个di>=w即可。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long LL; int n,q; LL s[100005],d[100005],sum[100005]; int main(){ scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%lld",&s[i]); sort(s+1,s+n+1); for(int i=1;i<n;++i) d[i]=s[i+1]-s[i]; sort(d+1,d+n); for(int i=1;i<n;++i) sum[i]=sum[i-1]+d[i]; scanf("%d",&q); while(q--){ LL t1,t2,w; scanf("%lld%lld",&t1,&t2); w=t2-t1+1; d[n]=w; int l=1,r=n,m; while(l<=r){ m=(l+r)>>1; if(d[m]>=w) r=m-1; else l=m+1; } printf("%lld ",sum[l-1]+w*(n-l+1)); } printf("\n"); return 0; }