• [CF487C] Prefix Product Sequence


    前言

    update 2021.6.17

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

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

    倒在最后的构造。

    题目

    CF

    洛谷

    讲解

    判断是否有解

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

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

    (n>4) 时,如果 (n) 是合数,则一定存在 (a e 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,jin[0,n-2],i e 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})

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

    注意(nle4)的时候要特判。

    代码

    冗长

    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
    1");return;} 
    	if(n == 2) {printf("YES
    1
    2");return;}
    	if(n == 3) {printf("YES
    1
    2
    3");return;}
    	if(n == 4) {printf("YES
    1
    3
    2
    4");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
    ");
    	for(int i = 0;i < (n-1)/2;++ i) Put(qpow(G,2*i,n),'
    '),Put(qpow(G,n-(2*(i+1)),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),正确性显然。

  • 相关阅读:
    企业在线学习平台开发02
    企业在线学习平台开发01
    20200807-1
    20200801-01
    16用户体验评价-补
    13第一阶段意见评审-补
    11单词统计-补
    09用户模板和用户场景-补
    08顶会热词统计-补
    时间 Java
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/13769465.html
Copyright © 2020-2023  润新知