1. 程式人生 > 實用技巧 >[思維構造] 題解 Koishi Loves Construction

[思維構造] 題解 Koishi Loves Construction

[思維構造] 題解 Koishi Loves Construction

題目連線

題意簡述

\(T\) 組詢問,每組詢問詢問是否存在長度為 \(n\) 的排列滿足字首和在模 \(n\) 意義下兩兩不同或者滿足字首積在模 \(n\) 意義下兩兩不同,如果存在,請構造。

\(1\le T\le 10,1\le n\le 10^5\)

題目分析

很好的一道題!

\(n=1\) 時,必然有解,在下面的討論中認為 \(n>1\)

首先考慮字首和滿足要求,顯然 \(n\) 必須放在第一個,否則就會出現不合法的情況,因為 \(n\) 在模 \(n\) 意義下等於 \(0\) ,如果將 \(n\)

放在第 \(i(i\ge 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,2\dots,n-6,5,n-4,3,n-2,1 \]

剛好用到了 \(1\dots n\) 每個一次。

接下來考慮字首積滿足要求,顯然 \(n\) 必須放在最後一個,因為 \(n\) 及其以後的字首積都是 \(0\) ,這也啟發我們,如果 \((n-1)!\equiv 0\pmod n\)\(!\) 是階乘),那麼無解,因為此時除了前 \(n\) 個數的字首積為 \(0\) 以外還存在字首積為 \(0\) 的情況。

\(n=4\) 時, \(3!\equiv 2 \pmod 4\) ,可以構造出一組解 \(1,3,2,4\) ,當 \(n>4\) 並且 \(n\)

為合數時,必然有 \((n-1)!\equiv 0\pmod n\) ,這個比較顯然,此時無解。

\(n\) 為素數時,考慮把字首積問題轉化成字首和問題,因為 \(n\) 是素數,必然存在原根 \(g\)\(g^{i}(1\le i\le n-1)\pmod n\) 能夠恰好取遍 \(1,\dots,n-1\) ,而 \(\prod_{}g^{a_i}=g^{\sum a_i},g^{n-1}\equiv 1\pmod n\) ,所以我們成功把字首積問題轉化成字首和問題了。

如何求原根?我使用的方法是列舉原根,然後判斷。判斷 \(g\) 是不是質數 \(p\) 的原根可以列舉 \(p-1\) 的每個質因數 \(q\) ,然後判斷 \(g^{\frac{p-1}{q}}\bmod 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(" \n"[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(" \n"[i==n]);
						write(n),pc('\n');
					}
					else
						puts("0");
				}
				else
					puts("0");
			}
		}
	}
	return 0;
}