把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷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;
}
posted @ 2019-04-15 09:31  TheLostWeak  阅读(576)  评论(0编辑  收藏  举报