P11211 随机数生成器 题解
前置知识:原根,exCRT。
首先 是容易的,直接相邻的除一下即可。
否则考虑询问除连续的 个数,分别为 。
首先特判掉存在 的情况,此时直接枚举 即可。
我们先求出 的一个原根 ,设离散对数 表示 。
否则我们枚举 ,并且 没有一个是 。
此时我们有 个线性同余方程:。
除掉 ,留下合法的方程,然后 exCRT 合并所有方程。
最终我们得到了 ,其中 是 的因子。
然后我们枚举 ,然后判断 是否合法即可。
复杂度是所有的 之和乘一些 exgcd 的 。
写个程序算一下,这个和的上界大概是不超过 的,即 ,常数稍微写优秀一点就足以通过了。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】