[思维构造] 题解 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}\) ,考虑构造这样一个前缀和序列:
这个序列差分后得到:
刚好用到了 \(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;
}