[Solution]2020.1103 序列查询
Describtion:
有一个无限长的序列,给定 $k$,$mo$,满足 $ x[i]=i \times k \space\space mod \space \space mo $ 询问这个序列第$ i $个数到第$ r $个数中,大于等于$ u $且小于等于$ v $的数有多少个。$ mod$ 表示取模,求余,如: $10 mod 4=2$, $10 mod 2=0 $ $10 mod 3=1$。
Input:
第一行六个数 $k$,$mo$,$i$,$r$,$u$,$v$,意义如上所述 。
Output:
一个数表示满足要求的数的个数。
Example:
3 10 4 7 2 8
30%数据满足所有参数$ \le 10^3 $
100%数据满足 $1\le$所有参数$ \le 10^{10} $,$i \le r$,$u \le v$
首先我们考虑$O(n)$的暴力做法
对于区间$[i,r]$,计算出每一个数,看它的值是否在$[u,v]$之间(代码过于简单),但这样做只能拿到三十分,回到题面我们观察题目数据,n为$10^{10}$,所以必须为$O(log_n)$的做法才能$AC$这道题,那么接下来我们开始思考正解:
$O(log_n)$的做法:
对于一个数列题,在思考时最常规的优化方法就是利用前缀和,考虑在本题中前缀和的用法
即:$F(i,j)$表示前i个数中值小于等于j的数的个数
利用小小的容斥定理,此时的问题就转换成了求$$(F(r,v)-F(r,u-1))-(F(i,v)-F(i,u-1))$$,而对于求$F(i,j)$似乎没有什么简便的算法,只能去遍历,但我们又只能有$O(log_n)$的复杂度,应该怎么去实现呢?回到题目,仔细观察这个无限长的序列,就会有一个惊人的发现:这个数列一定是有序的。我们用样例数据来算20个看一看:
1 0-9 2 0 3 6 9 2 5 8 1 4 7 3 4 10-19 5 0 3 6 9 2 5 8 1 4 7
非常明显这个数列从$0$开始是每十个一循环的,在$[4,7]$中有三个数:$2\ 5\ 8$在$[2,8]$中,那么在$[14,17]$同样也只有三个数在$[2,8]$中,但对于一个任意序列我们是不是都要求出它的循环节呢?我们继续分析这个序列:
1 //若我们认为的去规定这个循环节的长度,我们看会发生什么 2 //我们规定长度为4 3 0-11为: 4 0 3 6 9 5 2 5 8 1 6 4 7 0 3
我们可以清楚地看到每一个节的对应元素都增加了$2$,即:$$ \Delta = len*k\%m =2 $$(这个应该很好理解)
在第一个节中去找在值在$[2,8]$上的数为:$3\ 6 $
然后是第二个节:$ 2\ 5\ 8 $
这个时候我们发现在第二个节中寻找范围在$[2,8]$上的数,就相当于在第一个节中寻找$[0,6]$的数(这个应该也很好理解),在第一个节中$[0,6]$上的数为:$0\ 3\ 6$,一个伟大的发现诞生了
第二个节中的$2\ 5 \ 8$明显就是在$0\ 3 \ 6$的基础上都加了$2$而已,利用这个$\Delta$的值,我们就思考起来这个节的长度是不是和我们查询数的个数无关
我们继续考虑,在第三个节我们找$[2,8]$,利用前面发现的规律,就相当于在第一个节中找$[-2,4]$的数,此时出现了负数,但显然序列中不可能出现负数,我们可以思考$[-2,0]$这个负区间是怎么来的,它应该是一个数$p$,$p-num*delta$来的,而这个数一定不是负数,所以说明这个数加上$\Delta \times num$后一定大于$m$,所以这个数$%m$的值一定在$[-2+m,m)$之间(此处建议自己在纸上算一算),此时我们就把这个区间重新转换为了一个正区间,那么现在在第一个节中寻找的范围就应该是$[0,4]$和$[8,9]$,即:$0\ 3\ 6$,此时我们再次发现,$4\ 7 \ 3$就是由这三个数变来的,为了简化求在这个范围的数的个数,利用小小的容斥,就相当于是用循环节的长度-循环节中值在$[4,7]$上的数的个数
到目前为止,我们就已经总结出了如何通过只维护一个节来访问整个序列的方法
代码实现如下:
1 ll ans(ll k,ll m,ll n,ll l,ll r){ 2 if(n<0||r<0||l>=m)return 0; 3 if(r>m-1)r=m-1; 4 if(r-l+1==m)return n+1; 5 for(int i=0;i<step;i++){//step只与时间时间复杂度有关,取到根号1e10最好 6 a[i]=b[i]=k*i%m; //从0开始存前step项的序列 7 } 9 ll derta=(ll)step*k%m; //derta为全代码精华部分,代表每个step序列差值 10 sort(a,a+step); //前step个排序,便于二分查找(优化时间复杂度) 11 ll res=0,now; //rest计数,now控制遍历次数 12 for(now=0;now+step<=n+1;now+=step){//需要遍历n/step次 13 if(l<r)res+=efind(r)-efind(l-1);//二分查找在l-r之间的数的个数 14 else res+=step-(efind(l-1)-efind(r));//小小的容斥原理 15 l-=derta,r-=derta; //大段解释过的 16 if(l<0) l+=m; //l,r始终要大于0 17 if(r<0) r+=m; 18 } 19 for(ll i=now;i<=n;i++){ // 最 后 20 if( (l<=r && (b[i-now]>=l && b[i-now]<=r) ) || // 一 段 21 (l>r && (b[i-now]>=l || b[i-now]<=r) ) ) // 直 接 22 res++; // 暴 力 23 } 24 return res; 25 } 26 27 ll efind(ll k){ 28 int l=-1,r=step; 29 while(l+1<r){ 30 int mid=(l+r)/2; 31 if(a[mid]<=k)l=mid; 32 else r=mid; 33 } 34 return l+1; 35 }
主要功能实现后,整个程序就好实现了:
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define step 100000 4 using namespace std; 5 6 ll a[step+5],b[step+5]; 7 8 ll efind(ll k){ 9 int l=-1,r=step; 10 while(l+1<r){ 11 int mid=(l+r)/2; 12 if(a[mid]<=k)l=mid; 13 else r=mid; 14 } 15 return l+1; 16 } 17 18 ll ans(ll k,ll m,ll n,ll l,ll r){ 19 if(n<0||r<0||l>=m)return 0; 20 if(r>m-1)r=m-1; 21 if(r-l+1==m)return n+1; 22 for(int i=0;i<step;i++){//step只与时间时间复杂度有关,取到根号1e10最好 23 a[i]=b[i]=k*i%m; //存第一个循环节 24 } 25 ll derta=(ll)step*k%m; //derta为全代码精华部分,代表每个step序列变值 26 sort(a,a+step); //前step个排序,便于二分查找 27 ll res=0,now; //rest计数,now控制遍历次数 28 for(now=0;now+step<=n+1;now+=step){//需要遍历n/step次 29 if(l<r)res+=efind(r)-efind(l-1);//二分查找在l-r之间的数的个数 30 else res+=step-(efind(l-1)-efind(r));//小小的容斥原理 31 l-=derta,r-=derta; 32 if(l<0) l+=m; //l,r始终要大于0 33 if(r<0) r+=m; 34 } 35 for(ll i=now;i<=n;i++){ // 最 后 36 if( (l<=r && (b[i-now]>=l && b[i-now]<=r) ) || // 一 段 37 (l>r && (b[i-now]>=l || b[i-now]<=r) ) ) // 直 接 38 res++; // 暴 力 39 } 40 return res; 41 } 42 43 ll count(ll k,ll n,ll a,ll b,ll low,ll upp){ 44 return ans(k,n,b,low,upp)-ans(k,n,a-1,low,upp); //前缀和 45 } 46 47 int main(){ 48 ll k,n,l,r,u,v; 49 cin>>k>>n>>l>>r>>u>>v; // 输 入 50 cout<<count(k,n,l,r,u,v); // 输 出 51 return 0; // 完 结 撒 花 52 }
当然还有一个毒瘤的结构体写法:
1 #include <vector> 2 #include <list> 3 #include <map> 4 #include <set> 5 #include <queue> 6 #include <deque> 7 #include <stack> 8 #include <bitset> 9 #include <algorithm> 10 #include <functional> 11 #include <numeric> 12 #include <utility> 13 #include <sstream> 14 #include <iostream> 15 #include <iomanip> 16 #include <cstdio> 17 #include <cmath> 18 #include <cstdlib> 19 #include <ctime> 20 #include <cstring> 21 22 using namespace std; 23 24 const int step=100000; 25 long long A[step+5],B[step+5]; 26 27 class ModuleSequence 28 { 29 public: 30 31 long long dalc(long long key) 32 { 33 int l=-1,r=step; 34 while (l+1<r) 35 if (A[(l+r)/2]<=key) l=(l+r)/2; 36 else r=(l+r)/2; 37 return l+1; 38 } 39 40 long long calc(long long K,long long M,long long N,long long L,long long R) 41 { 42 if (N<0 || R<0 || L>=M) return 0; 43 if (R>=M-1) R=M-1; 44 if (R-L+1==M) return N+1; 45 46 for (int i=0;i<step;i++) A[i]=B[i]=(long long)K*i%M; 47 long long delta=(long long)step*K%M; 48 sort(A,A+step); 49 long long res=0,now; 50 for (now=0;now+step-1<=N;now+=step) 51 { 52 if (L<=R) res+=dalc(R)-dalc(L-1); 53 else res+=step-(dalc(L-1)-dalc(R)); 54 L-=delta;R-=delta; 55 if (L<0) L+=M;if (R<0) R+=M; 56 } 57 for (long long i=now;i<=N;i++) 58 res+=((L<=R && B[i-now]>=L && B[i-now]<=R) || ((L>R) && (B[i-now]>=L || B[i-now]<=R))); 59 return res; 60 } 61 62 long long countElements(long long K, long long N, long long A, long long B, long long lower, long long upper) 63 { 64 return calc(K,N,B,lower,upper)-calc(K,N,A-1,lower,upper); 65 } 66 }; 67 68 int main() { 69 freopen("seq.in","r",stdin); 70 freopen("seq.out","w",stdout); 71 long long k,n,l,r,u,v; 72 cin>>k>>n>>l>>r>>u>>v; 73 ModuleSequence a; 74 cout<<a.countElements(k,n,l,r,u,v); 75 return 0; 76 }
完结撒花!!!