CF487C Prefix Product Sequence

一、题目

点此看题

二、解法

解法一

首先我们来判断一下有无解,如果 n ≤ 4 n\leq 4 n4或者 n n n是质数的话就有解,否则无解。因为我们可以找到 a , b a,b a,b使得 n ∣ a b n|ab nab成立, a , b a,b a,b可以用如下方法取得:

  • 如果 n n n不是一个质数的完全平方数,那么一定存在 a b = n ab=n ab=n(把 n n n的因子分出去)
  • 否则 p 2 = n p^2=n p2=n,那么我们取 p p p 2 p 2p 2p就行了(这里 p > 2 p>2 p>2

因为是要构造出 [ 1 , n ] [1,n] [1,n]的所有数,所以可以利用原根,首位可以确定是 1 1 1,末尾可以确定是 n n n,然后用如下方法构造(注意 g n − 1 = 1 g^{n-1}=1 gn1=1):
g 0 , g n − 2 , g 2 , g n − 4 . . . . . . . n g^0,g^{n-2},g^2,g^{n-4}.......n g0,gn2,g2,gn4.......n那么前缀积就是:
g 0 , g n − 2 , g 1 , g n − 3 . . . . . . . n g^0,g^{n-2},g^1,g^{n-3}.......n g0,gn2,g1,gn3.......n如何求原根,先找到 φ \varphi φ的所有质因数,枚举可能的原根,用这些质因数去验证,不能存在:
g φ y s [ i ] = 1 m o d    n g^{\frac{\varphi}{ys[i]}}=1\mod n gys[i]φ=1modn因为不能提前出现数的循环(数两两不同),而 1 1 1就是出现循环的标志。

解法二

看能不能构造 1 , 2... n 1,2...n 1,2...n的前缀积,首项是 1 1 1,末项是 n n n,看 x x x x + 1 x+1 x+1
x a [ x + 1 ] = x + 1 m o d    n xa[x+1]=x+1\mod n xa[x+1]=x+1modn x ( a [ x + 1 ] − 1 ) = 1 m o d    n x(a[x+1]-1)=1\mod n x(a[x+1]1)=1modn逆元是一一对应的,构造成立,下面给出解法一的代码。

#include <cstdio>
const int M = 100005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,cnt,vis[M],p[M],g[M];
void sieve(int n)
{
	for(int i=2;i<=n;i++)
	{
		if(!vis[i]) p[++cnt]=i;
		for(int j=1;j<=cnt && i*p[j]<=n;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		} 
	}
}
int qkpow(int a,int b)
{
	int r=1;
	while(b>0)
	{
		if(b&1) r=r*a%n;
		a=a*a%n;
		b>>=1;
	}
	return r;
}
signed main()
{
	n=read();
	if(n==1) {puts("YES\n1");return 0;}
	if(n==2) {puts("YES\n1\n2");return 0;}
	if(n==3) {puts("YES\n1\n2\n3");return 0;}
	if(n==4) {puts("YES\n1\n3\n2\n4");return 0;}
	sieve(n);
	if(vis[n]) {puts("NO");return 0;}
	int phi=n-1,G=0;
	for(int i=1;i<=cnt;i++)
		if(phi%p[i]==0)
			g[++m]=p[i];
	for(int i=2;i<n;i++)
	{
		bool fuck=1;
		for(int j=1;j<=m;j++)
			if(qkpow(i,phi/g[j])==1) fuck=0;
		if(fuck) G=i;
	}
	puts("YES");
	for(int i=0;i<(n-1)/2;i++)
		printf("%lld\n%lld\n",qkpow(G,2*i),qkpow(G,n-2*(i+1)));
	printf("%lld\n",n);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值