zoukankan      html  css  js  c++  java
  • [思维构造] 题解 Koishi Loves Construction

    [思维构造] 题解 Koishi Loves Construction

    题目连接

    题意简述

    (T) 组询问,每组询问询问是否存在长度为 (n) 的排列满足前缀和在模 (n) 意义下两两不同或者满足前缀积在模 (n) 意义下两两不同,如果存在,请构造。

    (1le Tle 10,1le nle 10^5)

    题目分析

    很好的一道题!

    (n=1) 时,必然有解,在下面的讨论中认为 (n>1)

    首先考虑前缀和满足要求,显然 (n) 必须放在第一个,否则就会出现不合法的情况,因为 (n) 在模 (n) 意义下等于 (0) ,如果将 (n) 放在第 (i(ige 2)) 个,那么前 (i) 个的和就等于前 (i-1) 个的和。

    考虑到所有数的和为 (frac{n(n+1)}{2}) ,如果 (n) 为奇数,那么所有数的和在模 (n) 意义下就是 (0) ,当 (n>1) 时,显然无解,因为第一个位置放 (n) 后前缀和已经存在了 (0) 了。

    (n) 为偶数的时候,所有数的和在模 (n) 意义下为 (frac{n}{2}) ,考虑构造这样一个前缀和序列:

    [frac{n}{2}-frac{n}{2},dots,frac{n}{2}+3,frac{n}{2}-2,frac{n}{2}+1,frac{n}{2}-1,frac{n}{2} ]

    这个序列差分后得到:

    [n,n-1,2dots,n-6,5,n-4,3,n-2,1 ]

    刚好用到了 (1dots n) 每个一次。

    接下来考虑前缀积满足要求,显然 (n) 必须放在最后一个,因为 (n) 及其以后的前缀积都是 (0) ,这也启发我们,如果 ((n-1)!equiv 0pmod n)(!) 是阶乘),那么无解,因为此时除了前 (n) 个数的前缀积为 (0) 以外还存在前缀积为 (0) 的情况。

    (n=4) 时, (3!equiv 2 pmod 4) ,可以构造出一组解 (1,3,2,4) ,当 (n>4) 并且 (n) 为合数时,必然有 ((n-1)!equiv 0pmod n) ,这个比较显然,此时无解。

    (n) 为素数时,考虑把前缀积问题转化成前缀和问题,因为 (n) 是素数,必然存在原根 (g)(g^{i}(1le ile n-1)pmod n) 能够恰好取遍 (1,dots,n-1) ,而 (prod_{}g^{a_i}=g^{sum a_i},g^{n-1}equiv 1pmod n) ,所以我们成功把前缀积问题转化成前缀和问题了。

    如何求原根?我使用的方法是枚举原根,然后判断。判断 (g) 是不是质数 (p) 的原根可以枚举 (p-1) 的每个质因数 (q) ,然后判断 (g^{frac{p-1}{q}}mod p) 是否等于 (0) 即可。

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ch() getchar()
    #define pc(x) putchar(x)
    #include<assert.h>
    using namespace std;
    template<typename T>void read(T&x){
    	static char c;static int f;
    	for(c=ch(),f=1;c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c>='0'&&c<='9';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>void write(T x){
    	static char q[65];int cnt=0;
    	if(x<0)pc('-'),x=-x;
    	q[++cnt]=x%10,x/=10;
    	while(x)
    		q[++cnt]=x%10,x/=10;
    	while(cnt)pc(q[cnt--]+'0');
    }
    const int maxn=100005;
    int power(int a,int x,int mod){
    	int re=1;
    	while(x){
    		if(x&1)re=1ll*re*a%mod;
    		a=1ll*a*a%mod,x>>=1;
    	}
    	return re;
    }
    int st[maxn],tp;
    int getg(int p){
    	int o=sqrt(p-1),cp=p-1;tp=0;
    	for(int i=2;i<=o&&cp>1;++i){
    		if(cp%i==0){
    			st[++tp]=i;
    			while(cp%i==0)cp/=i;
    		}
    	}
    	if(cp>1)st[++tp]=cp;
    	for(int g=1;;++g){
    		int ok=true;
    		for(int i=1;i<=tp&&ok;++i)
    			ok&=(power(g,(p-1)/st[i],p)>1);
    		if(ok)return g;
    	}
    }
    int p[maxn];
    void solvesum(int n){
    	if(n==1)p[1]=1;
    	else if(n&1)p[1]=-1;
    	else{
    		for(int i=2;i<=n;i+=2)
    			p[i]=n-i+1;
    		for(int i=3;i<=n;i+=2)
    			p[i]=i-1;
    		p[1]=n;
    	}
    }
    int q[maxn],vis[maxn];
    int main(){
    	int x,t;
    	read(x),read(t);
    	if(x==1){
    		while(t--){
    			int n;read(n);
    			solvesum(n);
    			if(~p[1]){
    				write(2),pc(' ');
    				for(int i=1;i<=n;++i)
    					write(p[i]),pc(" 
    "[i==n]);
    			}
    			else
    				puts("0");
    		}
    	}
    	else{
    		while(t--){
    			int n;read(n);
    			if(n==1)puts("2 1");
    			else if(n==4)puts("2 1 3 2 4");
    			else{
    				int up=sqrt(n),cp=n,cnt=0;
    				for(int i=2;i<=up&cp>1;++i){
    					if(cp%i==0){
    						while(cp%i==0){
    							cp/=i;++cnt;
    						}
    					}
    				}
    				if(cp>1)++cnt;
    				if(cnt==1){
    					int g=getg(n);q[0]=1;
    					for(int i=1;i<n;++i)q[i]=1ll*q[i-1]*g%n;
    					solvesum(n-1);
    					if(~p[1]){
    						write(2),pc(' ');
    						for(int i=1;i<n;++i)
    							write(q[p[i]]),pc(" 
    "[i==n]);
    						write(n),pc('
    ');
    					}
    					else
    						puts("0");
    				}
    				else
    					puts("0");
    			}
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    10-2[RF] OOB validation
    5.css背景以及书写位置
    4.css基础
    3.表单form
    2.表格
    1.html基础
    正则表达式
    协程
    7.树与树算法
    6.排序与二分查找
  • 原文地址:https://www.cnblogs.com/lsq147/p/13924857.html
Copyright © 2011-2022 走看看