题目链接:https://www.luogu.com.cn/problem/P7325
题目大意:定义如下“广义斐波那契数列”:。
共有组询问,每组给出
和
,对于某个固定的模数
,求最小的
,使得
。
观察数据范围大概能猜出几点:
1、是固定的,因此很可能要预处理一堆东西来实现单组快速查询;
2、有一堆形如“是质数”“
不含平方因子”之类的部分分,这在数论题中是很强的暗示——很有可能需要对每个质因子处理之后CRH合并,同时处理质数幂可能比处理质数复杂一些。
斐波那契数列有一些很休闲的性质,比如我们通常会把它看作一个线性递推:从递推到
。
我们盯着二元组看,在模意义下这样的二元组数量是有限的,同时上述线性递推显然可逆,因此一定会形成纯循环。
从出发,我们的目标是推到某个
,如果
所在的循环节里有形如
的元素就行了,否则无解。
当然可以很trivial地把所有的循环节都找出来,并对于每个二元组记录一下它所在的循环节大小和到下一个的距离,然后直接查询就行。
只可惜二元组的数量是级别,因此还需要想办法。
等一下,不如我们先看看是质数时会发生什么?
这时候就需要一些想象力了:是质数有什么性质?额……比如模意义下所有非零元都与
互质?可以在模意义下随便作除法?
假设我们有一组询问是,那么把它变成
,其中
,会发生什么事?会对答案有影响吗?
嗯……好像没有影响啊。
这是因为,同乘之后会让数列里所有项都乘
,而如果
与
互质,容易证明
当且仅当
。
所以,如果我们面对一个,其中
非零(是零的话就不用算了),那就可以把它在模意义下同除以
,变成
。
我们惊奇地发现,原先级别的二元组数量被我们一下子砍成了
!
需要注意,不光起始状态可以这么操作,递推过程中的状态也可以,于是我们就对着这个元素处理循环节就行了。
下一步考虑不含平方因子的情况:对着每个质因子求一个答案,但是求出的答案当然不是唯一的,毕竟沿着循环节再多走几圈也行。
如果对于当前质因子,初始状态所在循环节大小为,到达第一个
的距离是
,那么最终答案只要满足
的形式就行。
这是什么?同余方程啊!所以把每个质因子求的结果exCRT一下就行了。
最后来考虑一下的情况:稍微麻烦之处在于不一定能作除法了。
不过问题不大,因为所有状态一定是形如的形式,其中
与
互质,
。
虽然的部分除不掉,但是
的部分还是能除的(需要一个exGCD),因此我们把所有二元组都化成
的形式就行了,这是
级别的。
需要注意三点:
一是容易证明两个和
一定不在同一个循环节里,因此最后求出的一定还是一个形如
的同余方程;
二是在这时压缩状态后的循环就不是纯循环了,而是形,需要在实现时特别注意;
三是需要特殊处理一下形如的状态。
总复杂度。
代码很丑而且细节很多:
#include<bits/stdc++.h>
#define gc getchar()
#define pc putchar
#define li long long
using namespace std;
inline li read(){
li x = 0;
int y = 0,c = gc;
while(c < '0' || c > '9') y = c,c = gc;
while(c >= '0' && c <= '9') x = x * 10 + c - '0',c = gc;
return y == '-' ? -x : x;
}
inline void prt(li x){
if(x >= 10) prt(x / 10);
pc(x % 10 + '0');
}
inline void print(li x){
if(x < 0) pc('-'),x = -x;
prt(x);
}
int n,m,a[100010],b[100010],cs[100010],cc[17];
int pi[15],ci[15],cnt;
int to[17][100010][2];
li an[17][100010][2],as[100010][3],mo;
inline li gcd(li x,li y){
return !y ? x : gcd(y,x % y);
}
inline li exgcd(li a,li b,li &x,li &y){
if(!b){
x = 1;y = 0;
return a;
}
li g = exgcd(b,a % b,x,y);
li t = x;x = y;y = t - a / b * y;
return g;
}
inline li lcm(li x,li y){
return x / gcd(x,y) * y;
}
void crt(li &p1,li &p2,li q1,li q2){
if(p2 == -2 || q2 == -2){
p2 = -2;return;
}
if(q1 == 1) return;
if(p1 == 1){
p1 = q1;p2 = q2;return;
}
li x,y,tmp = (p2 - q2 % p1 + p1) % p1,t2 = gcd(tmp,q1);
tmp /= t2;q1 /= t2;
if(exgcd(q1,p1,x,y) != 1){
p2 = -2;return;
}
if(x < 0) x += p1;
q1 *= t2;
li k = tmp * x % p1;
p1 = lcm(p1,q1);
p2 = (k * q1 + q2) % p1;
}
inline void nxt(int &a,int &b){
li u = b,v = (cc[a] + b) % mo,x,y;
int k = exgcd(u,mo,x,y),l = cs[k];
if(x < 0) x += mo / k;
(v *= x) %= mo;
a = l;b = v;
}
void dfs(int x,int y,int st){
if(!y){
an[x][y][0] = st + 1;
an[x][y][1] = 0;
return;
}
int tx = x,ty = y;
nxt(tx,ty);
dfs(tx,ty,st + 1);
an[x][y][0] = an[tx][ty][0];
an[x][y][1] = an[tx][ty][1] + 1;
}
void dfs2(int x,int y){
if(an[x][y][0] > 0) return;
if(an[x][y][0] == -1){
an[x][y][1] = -2;
return;
}
an[x][y][0] = -1;
int tx = x,ty = y;
nxt(tx,ty);
dfs2(tx,ty);
an[x][y][0] = an[tx][ty][0];
an[x][y][1] = an[tx][ty][1] + 1;
if(an[x][y][1] == -1) --an[x][y][1];
}
int main(){
int i,j,k,l;
li x,y;
n = read();m = read();
for(i = 1;i <= n;++i) a[i] = read() % m,b[i] = read() % m,as[i][0] = 1;
if(m == 1){
for(i = 1;i <= n;++i) pc('0'),pc('\n');
return 0;
}
int tmp = m;
for(i = 2;i * i <= tmp;++i) if(tmp % i == 0){
pi[++cnt] = i;
while(tmp % i == 0) ++ci[cnt],tmp /= i;
}
if(tmp != 1) pi[++cnt] = tmp,ci[cnt] = 1;
int tx,ty;
for(i = 1;i <= cnt;++i){
int p = pi[i],c = ci[i];
cc[0] = 1;
for(j = 1,k = p;j <= ci[i];++j,k *= p) cs[k] = j,cc[j] = k;
mo = cc[ci[i]];
for(j = 0;j < c;++j){
for(k = 0;k < mo;++k) an[j][k][0] = an[j][k][1] = 0;
}
for(j = 0;j < c;++j){
for(k = 0;k < mo;++k){
tx = j;ty = k;
nxt(tx,ty);
to[j][k][0] = tx;to[j][k][1] = ty;
}
}
for(j = 0;j < c;++j){
dfs(j,cc[j],1);
for(k = 0;k < mo;++k) if(!an[j][k][0]) dfs2(j,k);
}
for(j = 1;j <= n;++j){
li u = a[j] % mo,v = b[j] % mo;
if(!u && !v) continue;
bool fg = 0;
if(!u) swap(u,v),fg = 1;
k = exgcd(u,mo,x,y);l = cs[k];
if(x < 0) x += mo / k;
(v *= x) %= mo;
li a1 = an[l][v][0],a2 = an[l][v][1];
if(fg) a2 = a1 - 1;
as[j][2] = max(as[j][2],a2);
crt(as[j][0],as[j][1],a1,a2 == -2 ? a2 : a2 % a1);
}
}
for(i = 1;i <= n;++i){
if(!a[i]) pc('0');
else if(!b[i]) pc('1');
else{
if(as[i][1] != -2 && as[i][1] < as[i][2])
as[i][1] += ((as[i][2] - as[i][1] - 1) / as[i][0] + 1) * as[i][0];
print(as[i][1] + 1);
}
pc('\n');
}
return 0;
}