[CF487C] Prefix Product Sequence

前言

update 2021.6.17

  • 给一个数竞大佬做了这道题,他想了很久,但是这是他们书上的原题。。。

  • 更新了解法并优化了博客内容。

倒在最后的构造。

题目

CF

洛谷

讲解

判断是否有解

首先根据暴力我们不难看出只有当 \(n\le 4\) 或者 \(n\)质数的时候才有解。

当然我们也可以稍微证明一下。

\(n>4\) 时,如果 \(n\) 是合数,则一定存在 \(a\ne b,a,b<n,n|ab\) ,那么此后的前缀积为 \(0\) 了,直接GG,下面是对 \(a,b\) 的存在性的证明:

  • 如果\(n\)不是一个质数的完全平方数,则存在 \(ab=n\)
  • 如果\(n\)是一个质数的完全平方数,令 \(p^2=n\) ,显然 \(p>2\) ,则存在 \(a=p,b=2p\) ,那么 \(n|ab\)

构造求解

我们可以看出两个性质:

\(1\) 一定放在首位,放在中间的话一定会使得前一位的前缀积与当前的前缀积相等。

\(n\) 一定放在末位,如果放在前面的话会导致后面的前缀积都为 \(0\)

因为 \(n\) 为质数,下文的 \(n\) 都使用 \(p\) 代替。

考虑根据原根 \(g\) 具有的性质求解。

简单提一下原根的性质,对于 \(∀i,j\in[0,n-2],i\ne j\),不存在 \(g^i≡g^j \pmod p\)

我们只需将 \(g^0,g^1,...,g^{n-2}\) 输出即可。

吗?

然而这是前缀积,不能直接输出,根据\(g^{p-1}≡1 \pmod p\),我们有一个巧妙的构造方式。

并不是我想出来的。

我们只需按照这样的顺序排列即可:

\(g^0,g^{p-2},g^{2},g^{p-4},g^{4},...\)

如果将它们转化为前缀积的形式,再将指数对\(p-1\)取模那么可以化为:

\(g^0,g^{p-2},g^{1},g^{p-3},g^{2}\)

正确性不难证明,原根直接暴力找就好了。

注意\(n\le4\)的时候要特判。

代码

冗长

bool vis[MAXN];
int prime[MAXN],pn,ys[MAXN],tot;
void sieve(int x)
{
	for(int i = 2;i <= x;++ i)
	{
		if(!vis[i]) prime[++pn] = i;
		for(int j = 1;j <= pn && i * prime[j] <= x;++ j)
		{
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) break;
		}
	}
}
int qpow(int x,int y,int MOD)
{
	int ret = 1;
	while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
	return ret;
}
void solve3()
{
    //注意要特判
	if(n == 1) {printf("YES\n1");return;} 
	if(n == 2) {printf("YES\n1\n2");return;}
	if(n == 3) {printf("YES\n1\n2\n3");return;}
	if(n == 4) {printf("YES\n1\n3\n2\n4");return;}
    //想明白题目之前打的筛质数的板子,以为会用上,结果用处不大
	sieve(n);
    //大于4的合数无解
	if(vis[n]) {printf("NO");return;}
    //开始寻找原根
	int phi = n-1,G = 0;
	for(int i = 1;i <= pn && prime[i] <= phi;++ i)
		if(phi % prime[i] == 0)
			ys[++tot] = prime[i];
	for(int i = 2;i < phi && !G;++ i)
	{
		bool g = 1;
		for(int j = 1;j <= tot && g;++ j)
			if(qpow(i,phi/ys[j],n) == 1) g = 0;
		if(g) G = i;
	}
	//构造求解 
	printf("YES\n");
	for(int i = 0;i < (n-1)/2;++ i) Put(qpow(G,2*i,n),'\n'),Put(qpow(G,n-(2*(i+1)),n),'\n');
	Put(n);
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read();
	solve3();
	return 0;
}

后记

其实还有一种更简单的做法:\(1,\frac{2}{1},\frac{3}{2},\frac{4}{3},...,\frac{n-1}{n-2},n\),正确性显然。

posted @ 2020-10-05 10:26  皮皮刘  阅读(127)  评论(0编辑  收藏  举报