题意

给定$p$.

$q$ 次询问,给定 $a,b$ ,定义 $f_0=a,f_1=b,f_i=f_{i-1}+f_{i-2}$ ,求 $f_i\mod p=0$ 的最小整数解 $i$

$p,q 10^5$ ($p$不一定是质数)


首先我们知道,第一次出现循环节的位置是$O(p)$的。

对于一个询问 $a,b$ , 若 $\gcd(w,p)=1$ 则 $a,b$ 和 $aw,bw$ 等价。(显然第二个答案$\leq$第一个答案,然后由 $\gcd(x,p)=1$ 可得存在 $w^{-1} \bmod p$ )。 我们希望有一个 $x$ 能使得 $a$ 形式简单

如果 $p$ 是质数, $x=a^{-1} (\gcd(a,p)\not=1)$ 显然可行,那就可以转 $1,\frac{b}{a}$。 (否则write(1))

对于一个 $a$ ,我们可以 $\sum_{d|p}d = O(p\log \log p)$ 算出所有的 $a$ 答案,大概思路是遍历每个 $i$,然后知道可能的 $a$ 满足 $a\bmod q=r (q|p)$,然后最后从小到大遍历 $d$,显然可以由 $\bmod d$ 的答案转移到 $\bmod dx$ (当然要合并取min), $x$ 取 $d$外的最小因子即可(复杂度分析可能有点锅

然后 $a$ 不是质数的情况下,我们希望 $ax\bmod p=\gcd(a,p)$ ,直观想法大概是 exgcd(a,p) 得到 $x$。但这过不了大样例,为什么呢?因为 $\gcd(x,\frac{p,d})=1$ 但 $\gcd(x,p)$ 不一定 $=1$ ($d=\gcd(a,p)$)。

那有一个直观想法是 把 $x$ 里所有 $p$ 因子全删了,发现没有这个 $ax\bmod p$ 就重算,实测随机数据正确率 $\geq 99\%$ ,最终数据下80分(

然后有一个看上去靠谱的做法是,while(gcd(x,p)!=1) x+=p/d。 显然 $x\leftarrow x+\frac{p}{d}$ 对 $a$ 不会产生影响,只会对 $b$ 产生影响。

然后看上去跑的次数不是很多(

对于每个 $a$ 预处理 $x$ 没找到跑不过的数据(

如果有这一部分复杂度的证明或者hack欢迎来教育我,非常感谢

#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
tr1::unordered_map<ll,bool> st;
const int inf=0x3f3f3f3f;
int fib[1000005];
bool chkmax(int &a,int b){return a<b?a=b,1:0;}
bool chkmin(int &a,int b){return a>b?a=b,1:0;}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
void exgcd(int a,int b,int &x,int &y){
    if(!b){x=1,y=0;return;}
    exgcd(b,a%b,y,x);
    y-=(a/b)*x;
}
int lf[100000005],*at=lf;
int *newI(int x){int *ret=at;at+=x;return ret;}
int *f[100005];
int g[129][100005];
int nex[129];
int d[129],is[100005];
int q,p,m,c;
int ix[1000005],id[1000005];
void init(int a,int &x,int &d){
    int y;
    exgcd(a,p,x,y);
    d=gcd(a,p);
    if(x<0)x+=p/d;
}
void chk(int a,int b,int id){
    int x=ix[a];
    int d=::id[a];
    if(b)b=p-b;
    if(b%d)return;
    //if(x<0)x+=p/d;
    x=(ll)x*(b/d)%p;
    int w=p/d;
    chkmin(g[is[w]][x%w],id);
}
void solve(int w){
    for(int i=1;i<=m;++i)for(int j=0;j<d[i];++j)g[i][j]=inf;
    for(int i=1;i<=c;++i){
        chk(i-1,(ll)fib[i]*w%p,i);
    }
    for(int i=1;i<m;++i){
        int v=nex[i];
        for(int j=0;j<d[v];++j)chkmin(g[v][j],g[i][j%d[i]]);
    }
    for(int i=0;i<p;++i)f[is[w]][i]=g[m][i];
}
int val[100005];
int main(){
    #define make(x,y) ((ll)x*p+y)
    scanf("%d%d",&q,&p);
    if(p==1){
        for(;q;--q)printf("0\n");
        return 0;
    }
    fib[0]=fib[1]=1;
    st[make(1,1)]=1;
    for(int i=2;;++i){
        fib[i]=fib[i-1]+fib[i-2];
        if(fib[i]>=p)fib[i]-=p;
        ll w=make(fib[i-1],fib[i]);
        if(st[w]){c=i;break;}
        st[w]=1;
    }
    for(int i=0;i<c;++i){
        init(fib[i],ix[i],id[i]);
    }
    for(int i=1;i<=p;++i)if(!(p%i)){
        d[is[i]=++m]=i;
        f[m]=newI(p);
    }
    for(int i=1;i<m;++i)for(int j=i+1;j<=m;++j)if(!(d[j]%d[i])){nex[i]=j;break;}
    for(int x=1;x<m;++x){
        solve(d[x]);
    }
    int xm=m;
    for(int a=1;a<=p;++a){
        int x,y,g;
        exgcd(a,p,x,y);
        g=gcd(a,p);
        if(x<0)x+=p;
        while(gcd(x,p)>1){x+=p/g;if(x>=p)x-=p;}
        val[a]=x;
    }
    for(;q;--q){
        int a,b;
        scanf("%d%d",&a,&b);
        (b-=a)<0?b+=p:0;
        int v=gcd(a,p);
        if(v==p){printf("0\n");continue;}

        b=(ll)b*val[a]%p;
        v=(ll)a*val[a]%p;
        //assert(v==g);
        assert(is[v]);
        //if(!is[v]){d[is[v]=++xm]=v;f[xm]=newI(p);solve(v);}
//        printf("%d\n",(ll)a*x%p);
//        printf("%d\n",x);
        printf("%d\n",f[is[v]][b]==inf?-1:f[is[v]][b]);
    }
    //printf("%d\n",xm);
    return 0;
}

标签: none

添加新评论