前置知识:原根,exCRT。
首先 \(t=1\) 是容易的,直接相邻的除一下即可。
否则考虑询问除连续的 \(5\) 个数,分别为 \(a_0,a_1,\cdots,a_4\)。
首先特判掉存在 \(a_i=0\) 的情况,此时直接枚举 \(s\) 即可。
我们先求出 \(p\) 的一个原根 \(g\),设离散对数 \(\log(x)=y\) 表示 \(g^y\equiv x\pmod p\)。
否则我们枚举 \(x\),并且 \(x\sim x+4\) 没有一个是 \(\bmod p=0\)。
此时我们有 \(5\) 个线性同余方程:\(\forall 0\le i<5,s\log (x+i)\equiv \log a_i\pmod {p-1}\)。
除掉 \(\gcd\),留下合法的方程,然后 exCRT 合并所有方程。
最终我们得到了 \(s\equiv A\pmod B\),其中 \(B\) 是 \(p-1\) 的因子。
然后我们枚举 \(s=Bk+A,k\in [0,(p-1)/B)\),然后判断 \((s,x)\) 是否合法即可。
复杂度是所有的 \((p-1)/B\) 之和乘一些 exgcd 的 \(\log\)。
写个程序算一下,这个和的上界大概是不超过 \(2p\) 的,即 \(4\times 10^6\),常数稍微写优秀一点就足以通过了。
#include<bits/stdc++.h>
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
#define ff fflush(stdout)
using namespace std;
const int N=2e6+5;
int id,mod,g,a[5],lg[N];bool v[N];
inline int ask(){puts("?");ff;int x;scanf("%d",&x);return x;}
inline int md(int x){return x>=mod?x-mod:x;}
inline int ksm(int x,int p){int s=1;for(;p;(p&1)&&(s=1ll*s*x%mod),x=1ll*x*x%mod,p>>=1);return s;}
#define mytz __builtin_ctz
inline int gcd(int a,int b)
{
int az=mytz(a),bz=mytz(b),z=min(az,bz),diff;b>>=bz;
while(a) a>>=az,diff=a-b,az=mytz(diff),b=min(a,b),a=abs(diff);return b<<z;
}
namespace GG
{
int n;vector<int>g;
inline int ksm(int x,int p){int s=1;for(;p;(p&1)&&(s=1ll*s*x%n),x=1ll*x*x%n,p>>=1);return s;}
inline bool isy(int x){if(gcd(x,n)>1) return 0;for(int i:g) if(ksm(x,i)==1) return 0;return 1;}
inline int gg(int x)
{
g.clear();int t=(n=x)-1,y=t;
for(int i=2;i*i<=y;i++) if(y%i==0){while(y%i==0) y/=i;g.push_back(t/i);}(y^1)&&(g.push_back(t/y),1);
for(int i=1;i<x;i++) if(isy(i)) return i;
}
}//离散对数板子
inline bool chk(int x,int s){for(int i=0;i<5;i++) if(1ll*s*lg[md(x+i)]%(mod-1)!=a[i]) return 0;return 1;}
void exgcd(int a,int b,int &x,int &y)
{
if(!b) return x=1,y=0,void();
exgcd(b,a%b,x,y);int t=x;
x=y;y=(t-(a/b)*y);
}
inline int inv(int x,int p){int a,b;exgcd(x,p,a,b);return (a%p+p)%p;}
inline void mg(int &X,int &L,int x,int y)
{
if(!L) return L=x,X=y,void();int _x,_y,ll=L/gcd(L,x)*x;
exgcd(L,x,_x,_y);_x=(LL)(y-X)/gcd(L,x)*_x%ll;_x=(_x+ll)%ll;
X=((LL)L*_x+X)%ll;L=ll;
}//exgcd,excrt 板子
int main()
{
scanf("%d%d",&id,&mod);
if(id==1)
{
int A=ask(),B=ask();
return printf("! %d\n",1ll*B*ksm(A,mod-2)%mod),0;
}
g=GG::gg(mod);
for(int i=0,s=1;i<mod-1;i++,s=1ll*s*g%mod) lg[s]=i;//原根离散对数
for(int i=0;i<5;i++) a[i]=ask();
for(int i=0;i<5;i++) if(!a[i])
{
int x=md(mod-i);
for(int s=0;s<mod-1;s++) if(chk(x,s))
return printf("! %d\n",s),0;
}
for(int i=0;i<5;i++) a[i]=lg[a[i]];
for(int x=1;x+4<mod;x++)//枚举 x
{
int X=0,L=0;bool o=1;
for(int j=0;j<5;j++)
{
int t=md(j+x);
if(t==1) continue;
int A=lg[t],B=a[j],C=mod-1,g=gcd(A,C);
if(B%g){o=0;break;}A/=g,B/=g,C/=g;//除掉 gcd
mg(X,L,C,1ll*B*inv(A,C)%C);//excrt 合并同余方程
}
if(!o) continue;
for(int j=X;j<mod-1;j+=L) if(chk(x,j))
return printf("! %d\n",j),0;//枚举求解
}
printf("! 1\n");
return 0;
}
标签:P11211,return,int,题解,生成器,1ll,mytz,inline,mod
From: https://www.cnblogs.com/HaHeHyt/p/18488070