[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 }

完结撒花!!!

 

posted @ 2020-11-03 21:47  雪落た兮赏翩舞  阅读(117)  评论(0编辑  收藏  举报