【洛谷5285】[十二省联考2019] 骗分过样例(有趣的毒瘤题)
大致题意: 把所有输入输出数据都给你,并给你一定提示,让你写出正确的程序。
\(Case\ 1\sim Case\ 3\)
首先让我们点开数据,发现输入为\(0,1,2,3....\),输出为\(1,19,361,6859...\)。
不用多说,显然是求\(19^x\)。
再看功能编号\(1\_998244353\),显然\(998244353\)是模数。
于是,用快速幂就可以过前两个点。
然后就会发现第三个点的\(x\)很大。。。
因此就要借助欧拉定理来进行求解。
由欧拉定理可知:\(a^{\phi(MOD)}\equiv1(mod\ MOD)\)。
所以,\(a^x\equiv a^{x\%\phi(MOD)}(mod\ MOD)\)。
由于\(MOD\)是个质数,所以\(\phi(MOD)\)就等于\(MOD-1\)。
这样一来,我们只需要在读入\(x\)时边读边向\(MOD-1\)取模,即可。
代码如下:
I void Qmul(LL& x,CL y,CL X) {RL k=(LL)((1.0L*x*y)/(1.0L*X)),t=x*y-k*X;t-=X;W(t<0) t+=X;x=t;}//快速乘
I LL Qpow(RL x,RL y,CL X) {RL t=1;W(y) y&1&&(Qmul(t,x,X),0),Qmul(x,x,X),y>>=1;return t;}//快速幂
class CommonPow19Solver//求解19^x
{
public:
I void Solve(CL X)//求解答案,X为模数
{
RI n;RL x;for(F.read(n);n;--n)
F.read_with_X(x,X-1),F.writeln(Qpow(19,x,X));//读入时向X-1取模,然后快速幂
}
}Common;
\(Case\ 4\)
看数据显然还是求\(19^x\)。
但功能编号里本该放模数的位置变成了\(?\)是什么鬼?
仔细观察,似乎答案都不是很大。
因此我们可以考虑通过枚举的方式来确定模数。
然后有几点需要注意:
- 枚举下界是所有元素最大值(\(1145099\))\(+1\)。
- 验证时不要像我一开始那样\(naive\)地对于每个数去验证,其实只要验证一下第一个大数\(627811703016764290815178977207148434322\)的答案是否正确即可。
- 模数需要为质数,
因为我们要相信出题人很良心,不然\(\phi\)不好求。而判断其是否为质数可以使用\(MillerRabin\)。(我们会发现接下来经常要用到\(MillerRabin\))
于是就可以得到找模数代码如下:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define RL Reg LL
#define Con const
#define CI Con int&
#define CL Con LL&
#define I inline
#define W while
#define LL long long
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
#define Shl(x) ((x<<=1)>=X&&(x-=X))
using namespace std;
LL X;
class MillerRabin//MillerRabin判素数板子
{
private:
#define Pcnt 12
Con int P[Pcnt]={2,3,5,7,11,13,17,19,61,2333,4567,24251};
I LL Qmul(CL x,CL y,CL X)
{
RL k=(LL)((1.0L*x*y)/(1.0L*X)),t=x*y-k*X;
t-=X;W(t<0) t+=X;return t;
}
I LL Qpow(RL x,RL y,CL X)
{
RL t=1;W(y) y&1&&(t=Qmul(t,x,X)),x=Qmul(x,x,X),y>>=1;
return t;
}
I bool Check(CL x,CI p)
{
if(Qpow(p%x,x-1,x)^1) return false;
RL k=x-1,t;W(!(k&1))
{
if((t=Qpow(p%x,k>>=1,x))^1&&t^(x-1)) return false;
if(!(t^(x-1))) return true;
}return true;
}
public:
I bool IsPrime(CL x)
{
if(x<2) return false;
for(RI i=0;i^Pcnt;++i) {if(!(x^P[i])) return true;if(!Check(x,P[i])) return false;}
return true;
}
}MR;
I void Qmul(LL& t,RL y) {RL x=t;t=0;W(y) y&1&&Inc(t,x),Shl(x),y>>=1;}//快速乘
I LL Qpow(RL y) {RL x=19,t=1;W(y) y&1&&(Qmul(t,x),0),Qmul(x,x),y>>=1;return t;}//快速幂
I void Sread(Con string& s,LL& x,CL X)//从字符串中边取模边读入
{
for(RI i=x=0,l=s.length();i^l;++i)
x=(((x<<3)+(x<<1))%X+(s[i]&15))%X;
}
I void FindX(CL l,CL r)//寻找模数
{
RI n;RL i,x;Reg string st="627811703016764290815178977207148434322";//存下第一个大数
for(i=l;i<=r;++i) if(MR.IsPrime(X=i))//枚举数,判断其是否为质数
if(Sread(st,x,i-1),!(Qpow(x)^642666)) return (void)(printf("%lld",X));//读入,判断第一个数答案是否正确
}
int main() {return FindX(1145100,1500000),0;}//确定枚举范围
运行结果:
1145141
然后就可以套用之前的板子,只需要在调用时改成:
Common.Solve(1145141);
\(Case\ 5\)
\(1?+\)。。。一看就是\(1?\)的升级版。
我们可以发现模数似乎特别大。
暴枚肯定不行了,我们需要换一种思路。
考虑怎样才能确定模数的大致范围?
于是就能想到这样一种情况:找到最相近的两个数\(x,y\)(\(x<y\)),使得\(x\)的答案大于\(y\)的答案。
则我们通过\(x\)的答案求出\(y\)的答案在不取模情况下为\(ans_x*19^{y-x}\),则模数必然为\(ans_x*19^{y-x}-ans_y\)的因数。
具体实现就是将每个数与其对应的答案一起按数的大小排个序,然后扫一遍即可。
代码如下:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define RL Reg LL
#define Con const
#define CI Con int&
#define CL Con LL&
#define I inline
#define W while
#define N 10000
#define LL long long
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define eps 1e-10
#define ull unsigned long long
using namespace std;
int n;char st[5];struct data {LL x,v;I bool operator < (Con data& t) Con {return x<t.x;}}s[N+5];
I int Pow(CI x,CI y) {RI i,t=1;for(i=1;i<=y;++i) t*=x;return t;}
int main()
{
RI res=0;RL i,Mx=0;FILE *F1=fopen("software5.in","r"),*F2=fopen("software5.ans","r");
for(fscanf(F1,"%s%d",st,&n),i=1;i<=n;++i)
fscanf(F1,"%lld",&s[i].x),fscanf(F2,"%lld",&s[i].v),Gmax(Mx,s[i].v);//读入每个数及其对应的答案,Mx统计答案最大值
for(sort(s+1,s+n+1),i=1;i^n;++i) s[i].v>s[i+1].v&&//排序,然后找到符合条件的一对数
(!res||s[i+1].x-s[i].x<s[res+1].x-s[res].x)&&(res=i);
printf("Max=%lld\n%lld %lld\n%lld %lld\n",Mx,s[res].x,s[res].v,s[res+1].x,s[res+1].v);//输出
return 0;
}
运行结果:
Max=5211500658258874318
264708066 1996649514996338529
264708068 1589589654696467295
则可以计算出\(ans_y\)不取模为\(1996649514996338529*361=720790474913678208969\),然后减去取模后的\(ans_y\),得到模数为\(719200885258981741674\)的因数。
但对于这么大一个爆\(long\ long\)的数,我们该如何找它的因数呢?
没关系,我们有\(Python\),然后就可以枚举寻找一个较小的因数,来确定较大的那个因数。
但考虑到在根号范围内寻找依然太慢,于是要加两个优化:
- 我们要相信出题人足够良心,模数不会爆\(long\ long\),因此对于一开始模数爆\(long\ long\)的情况可以直接跳过。
- 我们之前不是得到了一个答案下界(即最大值\(+1\))吗?当另一个较大的因数小于等于最大值时,我们就可以直接结束循环了。
于是效率大大提高,很快就能得出答案。
代码如下:
v=719200885258981741674#已知答案是这个数的因数
Mx=5211500658258874319#答案下界
lim=2**63#long long范围
i=0#初始化i为0
while(1):
i+=1#将i加1
if(v//i>=lim):continue#如果此时答案超过long long范围,跳过
if(v%i==0):print (v//i)#如果找到答案,输出
if(v//i<Mx):break#如果小于答案下界,结束循环
运行结果:
5211600617818708273
然后照样套用之前的板子,只需改成:
Common.Solve(5211600617818708273);
\(Case\ 6\sim Case\ 7\)
这个就很有趣了,\(1wa\_998244353\),显然还是在模\(998244353\)意义下求\(19^x\),但这个\(wa\)是什么鬼?
点开输出,可以发现有负数!
便能想到或许是在暴力乘的时候写成了:
x=19*x%X;
然而19*x
爆\(int\)了,于是就有了负数。
但这样显然不能快速幂。
不过,这样或许会出现循环节!
于是就可以想到借助\(map\),写出这样一份代码:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define RL Reg LL
#define Con const
#define CI Con int&
#define CL Con LL&
#define I inline
#define W while
#define N 10000
#define LL long long
#define X 998244353
using namespace std;
map<int,int> p;
int main()
{
for(RI i=1,x=1;;++i)
if(p[x=19*x%X]) return printf("%d %d",p[x],i-p[x]),0;//找到循环节则输出开始循环的位置和周期
else p[x]=i;return 0;//否则,继续处理
}
运行结果:
55245 45699
然后就可以写出求解这个问题的代码了:
class OverFlowPow19Solver//求解溢出情况下19^x
{
private:
#define A 55244//开始循环的位置
#define B 45699//循环周期
#define X 998244353//模数
int a[A+B+5];
public:
I void Solve()
{
RI n,i;RL x;for(a[0]=i=1;i<=A+B;++i) a[i]=19*a[i-1]%X;//打表
for(F.read(n);n;--n) F.read(x),F.writeln(a[x<=A?x:(x-A)%B+A]);//输出答案
}
#undef X
}OverFlow;
\(Case\ 8\sim Case\ 10\)
看到功能编号变成了\(2\),说明接下来就不是求\(19^x\)了。
首先,研究数据可得,\(p\)为\(Prime\)(素数),然后题目要求就是对一段区间内质数输出\(p\),合数输出\(.\)。
判断大素数就可以用之前提到过的\(MillerRabin\),详见此博客:初学MillerRabin素数测试。
然后声明一下,这道题中我只用2和3来测试理论上来讲是不够严谨的,但错误概率小,依然过了。
而且主要是为了下一个部分分卡常需要。。。
代码如下:
class MillerRabin//MillerRabin判素数板子
{
private:
I bool Check(CL x,CI p)
{
if(Qpow(p%x,x-1,x)^1) return false;
RL k=x-1,t;W(!(k&1))
{
if((t=Qpow(p%x,k>>=1,x))^1&&t^(x-1)) return false;
if(!(t^(x-1))) return true;
}return true;
}
public:
I bool IsPrime(CL x)
{
return !(x^2)||!(x^3)||(x&1&&x%3&&Check(x,2)&&Check(x,3));
}
}MR;
class PrimeSolver//判断一段区间内每个数是否为质数
{
public:
I void Solve()
{
RI n;RL i,l,r;for(F.read(n);n;--n,F.writec('\n'))
for(F.read(l,r),i=l;i<=r;++i) F.writec(MR.IsPrime(i)?'p':'.');//输出
}
}Prime;
\(Case\ 11\sim Case\ 13\)
看到\(u\),以及输出为\(0,+,-\),便不难想到是求莫比乌斯函数\(\mu\)。
关于莫比乌斯函数的定义可以参考这篇博客:初学莫比乌斯反演。
观察可得,区间的左边界和右边界虽然值域高达\(10^{18}\),但是实际区间长度却很小。
则我们可以考虑先用线性筛筛出\(10^6\)以内所有质数及所有数的\(\mu\)值。
然后,对于小于等于\(10^6\)的询问,直接输出筛到的\(\mu\)。
否则,先把其所有小于等于\(10^6\)的质因数全部除去,同时统计\(\mu\)值。
然后,最后得到的这个数要么为\(1\),否则显然由最多两个大于\(10^6\)的质数组成。
于是,进行以下操作:
- 用\(MillerRabin\)判断其是否为质数,若是,将其先前统计的\(\mu\)值乘上\(-1\)然后输出。
- 否则,我们判断其是否为完全平方数,若是,则说明它是一个质数的平方,因此将其\(\mu\)值改为\(0\)。
- 否则,它由两个互不相同的质数组成,因此将其先前统计的\(\mu\)值乘上\((-1)*(-1)\),就相当于乘\(1\),无需任何操作。
然后注意卡常。
具体代码如下:
class LineSiever//线性筛
{
private:
#define SZ 1000000
public:
int Pc,mu[SZ+5],P[SZ+5];
I void Sieve(CI S)
{
for(RI i=(mu[1]=1,2),j;i<=S;++i)
for(!P[i]&&(mu[P[++Pc]=i]=-1),j=1;j<=Pc&&1LL*i*P[j]<=S;++j)
if(P[i*P[j]]=1,i%P[j]) mu[i*P[j]]=-mu[i];else break;
}
}L;
class MuSolver//求一段区间内每个数的μ值
{
private:
#define S 1000000
#define C(x) ((x)?(~(x)?'+':'-'):'0')
int g[S+5];ull v[S+5];
public:
I void Solve()
{
RI n,i,j,lim,len;RU x,y,l,r;for(L.Sieve(S),F.read(n);n;--n)
{
if(F.read(l,r),l<=S)//对于筛过的数,直接输出μ值
{
for(i=l,lim=min(r,S);i<=lim;++i) F.writec(C(L.mu[i]));
if(F.writec('\n'),r<=S) continue;l=S+1;
}
for(len=r-l+1,i=len;i;--i) g[i]=v[i]=1;//初始化
for(i=L.Pc;i;--i) for(j=(l+L.P[i]-1)/L.P[i]*L.P[i]-l+1;j<=len;j+=L.P[i])//枚举质数及其倍数
(l+j-1)/L.P[i]%L.P[i]?g[j]&&(g[j]*=-1,v[j]*=L.P[i]):(g[j]=0);//统计μ值
for(i=1;i<=len;++i) g[i]&&(i+l-1)^v[i]&&(MR.IsPrime(x=(i+l-1)/v[i])?//如果不为1
g[i]*=-1:(y=sqrt(x),y*y==x)&&(g[i]=0)),F.writec(C(g[i]));//为质数则将μ值乘-1,为完全平方数则将μ值变为0,然后输出
F.writec('\n');
}
}
#undef S
}Mu;
\(Case\ 14\)
\(g=\)原根,这就相当于要我们求一段区间内每个数是否为给定质数的原根。
考虑原根定义:一个数\(x\)是质数\(p\)的原根,当且仅当对于\(\phi(p)\)(即\(p-1\))的任意质因数\(q\),\(x^{\frac{\phi(p)}q}\%p≠1\)。
发现这个点的模数全为\(998244353\),对此直接暴力分解\(998244352\),然后暴力判断即可:
class GRSolver//判断一段区间内每个数是否为给定质数的原根
{
private:
#define S 100000
int cnt,s[S+5];
I void Init(CI x)//分解
{
RI i,t=x;for(cnt=0,i=1;1LL*L.P[i]*L.P[i]<=t;++i)
if(!(t%L.P[i])) {s[++cnt]=L.P[i];W(!(t%L.P[i])) t/=L.P[i];}//存储质因数
t^1&&(s[++cnt]=t);//存储质因数
}
I bool IsGR(CI x,CI X)//暴力判断
{
for(RI i=1;i<=cnt;++i) if(Qpow(x,(X-1)/s[i],X)==1) return false;
return true;
}
public:
I void Solve()
{
RI n,i,l,r,X;for(L.Sieve(S),F.read(n);n;--n)
for(Init(X-1),i=l;i<=r;++i) F.writec(IsGR(i,X)?'g':'.');//暴力
}
}GR;
\(Case\ 15\)
这个点在原有模数基础上多了个\(13123111\)。
然后暴力就跑不过了。。。
于是我们就要用一种特殊的方式来做。
首先,我们暴力找出其最小的一个原根(实际上任意一个原根都可以)。
接下来求出每个数以这个最小原根为原根的指标。
然后判断每个数的指标与\(\phi(X)\)(即\(X-1\))是否互质即可判断这个数是否为原根。
但判互质这里需要一种类似于筛的东西。
考虑和上个点一样先求出\(X-1\)的全部质因数,然后把这些质因数的倍数全部打个标记。
最后指标没被打标记的就是原根。
与上一个点整合后的代码如下:
class GRSolver//判断一段区间内每个数是否为给定质数的原根
{
private:
#define S 100000
#define V 13123111
int cnt,s[S+5],pos[V+5],vis[V+5];
I void Init(CI x)//分解
{
RI i,t=x;for(cnt=0,i=1;1LL*L.P[i]*L.P[i]<=t;++i)
if(!(t%L.P[i])) {s[++cnt]=L.P[i];W(!(t%L.P[i])) t/=L.P[i];}//存储质因数
t^1&&(s[++cnt]=t);//存储质因数
}
I bool IsGR(CI x,CI X)//暴力判断
{
for(RI i=1;i<=cnt;++i) if(Qpow(x,(X-1)/s[i],X)==1) return false;
return true;
}
I void Sieve(CI l,CI r,CI X)//筛
{
static int T=0;RI i,j,p,t;++T;//初始化
for(t=1;!IsGR(t,X);++t);for(i=1,p=t;i^X;++i) pos[p]=i,p=1LL*p*t%X;//暴力找到一个最小的原根,然后求指标
for(i=1;i<=cnt;++i) for(j=s[i];j<X;j+=s[i]) vis[j]=T;//打标记
for(i=l;i<=r;++i) F.writec(vis[pos[i]]^T?'g':'.');//输出
}
public:
I void Solve()
{
RI n,i,l,r,X;for(L.Sieve(S),F.read(n);n;--n)
{
if(X==998244353) for(Init(X-1),i=l;i<=r;++i) F.writec(IsGR(i,X)?'g':'.');//暴力
else Init(X-1),Sieve(l,r,X);F.writec('\n');//筛
}
}
}GR;
\(Case\ 16\)
\(2g?\),又是一个要确定模数的,不过这次良心地给了模数的范围,只不过范围大小是\(10^9\)。。。
考虑出题人会怎么卡你?肯定是把模数放在靠中间的位置。
因此,我们就从中间向两边枚举!
然后对于枚举到的数如何判断呢?
首先依然是\(MillerRabin\)判素数。
接下来,我们只验证前\(50\)个答案,如果这个数的\(\frac {x-1}2\)次方为\(1\)但是这一位应该是原根,就说明\(x\)不是要求的模数。
代码如下:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define M 1500000000
#define S 50
using namespace std;
Con string s="...ggg..gg.g...g..gg..g.gg......gg..g......gg..gg.";
I int Qpow(RI x,RI y,CI X) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
class MillerRabin//MillerRabin判素数板子
{
private:
#define Pcnt 12
Con int P[Pcnt]={2,3,5,7,11,13,17,19,61,2333,4567,24251};
I bool Check(CI x,CI p)
{
if(Qpow(p%x,x-1,x)^1) return false;
RI k=x-1,t;W(!(k&1))
{
if((t=Qpow(p%x,k>>=1,x))^1&&t^(x-1)) return false;
if(!(t^(x-1))) return true;
}return true;
}
public:
I bool IsPrime(CI x)
{
if(x<2) return false;
for(RI i=0;i^Pcnt;++i) {if(!(x^P[i])) return true;if(!Check(x,P[i])) return false;}
return true;
}
}MR;
I bool Check(CI X)//验证前50个答案
{
for(RI i=0;i^S;++i) if(Qpow(i+233333333,X-1>>1,X)==1&&s[i]^'.') return false;//产生冲突就返回false
return true;//返回true
}
int main()
{
for(RI i=1;;i+=2)//从中间向外枚
{
if(MR.IsPrime(M+i)&&Check(M+i)) return printf("%d",M+i),0;//先判质数,再验证
if(MR.IsPrime(M-i)&&Check(M-i)) return printf("%d",M-i),0;//同上
}return 0;
}
运行结果:
1515343657
再与先前代码一整合,就可以得到这一部分的最终代码:
class GRSolver//判断一段区间内每个数是否为给定质数的原根
{
private:
#define S 100000
#define V 13123111
int cnt,s[S+5],pos[V+5],vis[V+5];
I void Init(CI x)//分解
{
RI i,t=x;for(cnt=0,i=1;1LL*L.P[i]*L.P[i]<=t;++i)
if(!(t%L.P[i])) {s[++cnt]=L.P[i];W(!(t%L.P[i])) t/=L.P[i];}//存储质因数
t^1&&(s[++cnt]=t);//存储质因数
}
I bool IsGR(CI x,CI X)//暴力判断
{
for(RI i=1;i<=cnt;++i) if(Qpow(x,(X-1)/s[i],X)==1) return false;
return true;
}
I void Sieve(CI l,CI r,CI X)//筛
{
static int T=0;RI i,j,p,t;++T;//初始化
for(t=1;!IsGR(t,X);++t);for(i=1,p=t;i^X;++i) pos[p]=i,p=1LL*p*t%X;//暴力找到一个最小的原根,然后求指标
for(i=1;i<=cnt;++i) for(j=s[i];j<X;j+=s[i]) vis[j]=T;//打标记
for(i=l;i<=r;++i) F.writec(vis[pos[i]]^T?'g':'.');//输出
}
public:
I void Solve()
{
RI n,i,l,r,X;for(L.Sieve(S),F.read(n);n;--n)
{
if((!F.read(l,r,X)&&(X=1515343657))||(X==998244353))//对于?赋值为1515343657
for(Init(X-1),i=l;i<=r;++i) F.writec(IsGR(i,X)?'g':'.');//暴力
else Init(X-1),Sieve(l,r,X);F.writec('\n');//筛
}
}
}GR;
代码
最后放一下完整代码:
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define RL Reg LL
#define RU Reg ull
#define Con const
#define CI Con int&
#define CL Con LL&
#define CU Con ull&
#define I inline
#define W while
#define LL long long
#define ull unsigned long long
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
string op;
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C^FS?FO[C++]=c:(fwrite(FO,1,C,stdout),FO[(C=0)++]=c))
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T,C;char c,*A,*B,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI;}
Tp I bool read(Ty& x) {x=0;W(!D) if(c=='?') return false;W(x=tn+(c&15),D);return true;}
Tp I void write(Ty x) {x<0&&(pc('-'),x=-x);W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Ts I bool read(Ty& x,Ar&... y) {return read(x)&&read(y...);}
Tp I void writeln(Con Ty& x) {write(x),pc('\n');}
Tp I void read_with_X(Ty& x,CL X) {x=0;W(!D);W(x=(tn%X+(c&15))%X,D);}
I void reads(string& x) {x="";W(isspace(c=tc()));W(x+=c,!isspace(c=tc())&&~c);}
I void writec(Con char& x) {pc(x);}
I void clear() {fwrite(FO,1,C,stdout),C=0;}
}F;
I void Qmul(LL& x,CL y,CL X) {RL k=(LL)((1.0L*x*y)/(1.0L*X)),t=x*y-k*X;t-=X;W(t<0) t+=X;x=t;}//快速乘
I LL Qpow(RL x,RL y,CL X) {RL t=1;W(y) y&1&&(Qmul(t,x,X),0),Qmul(x,x,X),y>>=1;return t;}//快速幂
class MillerRabin//MillerRabin判素数板子
{
private:
I bool Check(CL x,CI p)
{
if(Qpow(p%x,x-1,x)^1) return false;
RL k=x-1,t;W(!(k&1))
{
if((t=Qpow(p%x,k>>=1,x))^1&&t^(x-1)) return false;
if(!(t^(x-1))) return true;
}return true;
}
public:
I bool IsPrime(CL x)
{
return !(x^2)||!(x^3)||(x&1&&x%3&&Check(x,2)&&Check(x,3));
}
}MR;
class LineSiever//线性筛
{
private:
#define SZ 1000000
public:
int Pc,mu[SZ+5],P[SZ+5];
I void Sieve(CI S)
{
for(RI i=(mu[1]=1,2),j;i<=S;++i)
for(!P[i]&&(mu[P[++Pc]=i]=-1),j=1;j<=Pc&&1LL*i*P[j]<=S;++j)
if(P[i*P[j]]=1,i%P[j]) mu[i*P[j]]=-mu[i];else break;
}
}L;
class CommonPow19Solver//求解19^x
{
public:
I void Solve(CL X)//求解答案,X为模数
{
RI n;RL x;for(F.read(n);n;--n)
F.read_with_X(x,X-1),F.writeln(Qpow(19,x,X));//读入时向X-1取模,然后快速幂
}
}Common;
class OverFlowPow19Solver//求解溢出情况下19^x
{
private:
#define A 55244//开始循环的位置
#define B 45699//循环周期
#define X 998244353//模数
int a[A+B+5];
public:
I void Solve()
{
RI n,i;RL x;for(a[0]=i=1;i<=A+B;++i) a[i]=19*a[i-1]%X;//打表
for(F.read(n);n;--n) F.read(x),F.writeln(a[x<=A?x:(x-A)%B+A]);//输出答案
}
#undef X
}OverFlow;
class PrimeSolver//判断一段区间内每个数是否为质数
{
public:
I void Solve()
{
RI n;RL i,l,r;for(F.read(n);n;--n,F.writec('\n'))
for(F.read(l,r),i=l;i<=r;++i) F.writec(MR.IsPrime(i)?'p':'.');//输出
}
}Prime;
class MuSolver//求一段区间内每个数的μ值
{
private:
#define S 1000000
#define C(x) ((x)?(~(x)?'+':'-'):'0')
int g[S+5];ull v[S+5];
public:
I void Solve()
{
RI n,i,j,lim,len;RU x,y,l,r;for(L.Sieve(S),F.read(n);n;--n)
{
if(F.read(l,r),l<=S)//对于筛过的数,直接输出μ值
{
for(i=l,lim=min(r,S);i<=lim;++i) F.writec(C(L.mu[i]));
if(F.writec('\n'),r<=S) continue;l=S+1;
}
for(len=r-l+1,i=len;i;--i) g[i]=v[i]=1;//初始化
for(i=L.Pc;i;--i) for(j=(l+L.P[i]-1)/L.P[i]*L.P[i]-l+1;j<=len;j+=L.P[i])//枚举质数及其倍数
(l+j-1)/L.P[i]%L.P[i]?g[j]&&(g[j]*=-1,v[j]*=L.P[i]):(g[j]=0);//统计μ值
for(i=1;i<=len;++i) g[i]&&(i+l-1)^v[i]&&(MR.IsPrime(x=(i+l-1)/v[i])?//如果不为1
g[i]*=-1:(y=sqrt(x),y*y==x)&&(g[i]=0)),F.writec(C(g[i]));//为质数则将μ值乘-1,为完全平方数则将μ值变为0,然后输出
F.writec('\n');
}
}
#undef S
}Mu;
class GRSolver//判断一段区间内每个数是否为给定质数的原根
{
private:
#define S 100000
#define V 13123111
int cnt,s[S+5],pos[V+5],vis[V+5];
I void Init(CI x)//分解
{
RI i,t=x;for(cnt=0,i=1;1LL*L.P[i]*L.P[i]<=t;++i)
if(!(t%L.P[i])) {s[++cnt]=L.P[i];W(!(t%L.P[i])) t/=L.P[i];}//存储质因数
t^1&&(s[++cnt]=t);//存储质因数
}
I bool IsGR(CI x,CI X)//暴力判断
{
for(RI i=1;i<=cnt;++i) if(Qpow(x,(X-1)/s[i],X)==1) return false;
return true;
}
I void Sieve(CI l,CI r,CI X)//筛
{
static int T=0;RI i,j,p,t;++T;//初始化
for(t=1;!IsGR(t,X);++t);for(i=1,p=t;i^X;++i) pos[p]=i,p=1LL*p*t%X;//暴力找到一个最小的原根,然后求指标
for(i=1;i<=cnt;++i) for(j=s[i];j<X;j+=s[i]) vis[j]=T;//打标记
for(i=l;i<=r;++i) F.writec(vis[pos[i]]^T?'g':'.');//输出
}
public:
I void Solve()
{
RI n,i,l,r,X;for(L.Sieve(S),F.read(n);n;--n)
{
if((!F.read(l,r,X)&&(X=1515343657))||(X==998244353))//对于?赋值为1515343657
for(Init(X-1),i=l;i<=r;++i) F.writec(IsGR(i,X)?'g':'.');//暴力
else Init(X-1),Sieve(l,r,X);F.writec('\n');//筛
}
}
}GR;
int main()
{
if(F.reads(op),op=="1_998244353") Common.Solve(998244353);
else if(op=="1?") Common.Solve(1145141);
else if(op=="1?+") Common.Solve(5211600617818708273);
else if(op=="1wa_998244353") OverFlow.Solve();
else if(op=="2p") Prime.Solve();else if(op=="2u") Mu.Solve();else GR.Solve();
return F.clear(),0;
}