题解 P3599 【Koishi Loves Construction】
George1123 · · 题解
题解-Koishi Loves Construction
博客中阅读
前缀知识
质数 逆元 暴搜
Koishi Loves Construction
给定
X ,T 组测试数据,每次给一个n 。
- 如果
X=1 ,构造一个1\sim n 的排列使得前缀和模n 互不相同。- 如果
X=2 ,构造一个1\sim n 的排列使得前缀积模n 互不相同。数据范围:
1\le T\le 10 ,1\le n\le 10^5 ,X\in\{1,2\} 。
属于“思维体操”,做之前会异常兴奋,做之后会只想睡觉。
设序列为
分类讨论:
X=1
初步发现:
-
不能有区间
[L,R](L\neq 1) 和是n 的倍数,否则sum_{L-1}\equiv sum_R\pmod n 。 -
设
a_i=n ,必须i=1 ,否则sum_i\equiv sum_{i-1}\pmod n 。 -
然后由此判断输出
于是开始打暴力:
#include <bits/stdc++.h>
using namespace std;
//&Start
#define lng long long
#define lit long double
#define re register
#define kk(i,n) " \n"[i==n]
const int inf=0x3f3f3f3f;
const lng Inf=0x3f3f3f3f3f3f3f3f;
//&Data
const int N=1e5;
int a[N+10],n;
bool vis[N+10],use[N+10];
void dfs(int x,int sm){
if(x==n+1){
for(int i=1;i<=n;i++)
printf("%d%c",a[i],kk(i,n));
return ;
}
for(int i=1;i<=n;i++)
if(!use[i]){
use[i]=1;
if(!vis[(sm+i)%n]){
vis[(sm+i)%n]=1;
a[x]=i;
dfs(x+1,(sm+i)%n);
vis[(sm+i)%n]=0;
}
use[i]=0;
}
}
//&Main
int main(){
scanf("%d",&n);
dfs(1,0);
return 0;
}
/***
input
6
output
6 1 4 3 2 5
6 2 5 3 1 4
6 4 1 3 5 2
6 5 2 3 4 1
***/
输入
6 1 4 3 2 5
得出规律:
- 如果
i\in \mathbb{odd} ,a_i=n+1-i 。 - 如果
i\in \mathbb{even} ,a_i=i-1 。
//&Solve1
void solve1(){
memset(a,0,sizeof a);
memset(sum,0,sizeof sum);
if((n&1)&&(n^1)) return puts("0"),void();
else {
putchar('2'),putchar(' ');
for(int i=1;i<=n;i++)
printf("%d%c",(i&1)?n+1-i:i-1,kk(i,n));
}
}
提交后得到
证明:
模
0 1 -2 3 -4 5
很明显:
- 如果
i\in\mathbb{odd} ,sum_i\in\{0,-1,-2,...\} 。 - 如果
i\in\mathbb{even} ,sum_i\in\{1,2,3,...\} 。
最后在模
X=2
初步发现:
- 不能有区间
[L,R](L\neq 1) 的积\equiv 1\pmod n 。 - 不能有区间
[L,R](R\neq n) 的积\equiv 0\pmod n 。 -
-
- 还有如果
n|\prod\limits_{i=1}^{n-1}i 也不行,很明显。
然后由此判断输出
至于序列长什么样,暴力再来一发:
#include <bits/stdc++.h>
using namespace std;
//&Start
#define lng long long
#define lit long double
#define re register
#define kk(i,n) " \n"[i==n]
const int inf=0x3f3f3f3f;
const lng Inf=0x3f3f3f3f3f3f3f3f;
//&Data
const int N=1e5;
int a[N+10],n;
bool vis[N+10],use[N+10];
void dfs(int x,int sm){
if(x==n+1){
for(int i=1;i<=n;i++)
printf("%d%c",a[i],kk(i,n));
return ;
}
for(int i=1;i<=n;i++)
if(!use[i]){
use[i]=1;
if(!vis[(sm*i)%n]){
vis[(sm*i)%n]=1;
a[x]=i;
dfs(x+1,(sm*i)%n);
vis[(sm*i)%n]=0;
}
use[i]=0;
}
}
//&Main
int main(){
scanf("%d",&n);
dfs(1,1);
return 0;
}
/***
input
7
output
1 2 5 6 3 4 7
1 3 4 6 2 5 7
1 4 3 6 5 2 7
1 5 2 6 4 3 7
input
11
output
try it by yourself!
***/
进一步推测:如果
由此判断输出
然后我下了一下数据,竟然发现输出数据只有
0\&1 。再进一步发现:第二个数只能是
2 ,没用。
这时看输出(我看了
1 2 5 6 3 4 7
//前缀积%n:
1 2 3 4 5 6 0
有一个发现:
然后我茅塞顿开:可以试试逆元求序列使得
void solve2(){
if(np[n]&&(n^1)&&(n^4)) return puts("0"),void();
else {
if(n==1) return puts("2 1"),void();
if(n==4) return puts("2 1 3 2 4"),void();
putchar('2');
for(int i=1,tmp=1,sum=1;i<=n-1;i++){
printf(" %d",tmp);
tmp=1ll*Pow(sum,n-2)*(i+1)%n;
sum=1ll*sum*tmp%n;
}
printf(" %d",n),putchar('\n');
}
}
提交了一发,
证明:
很明显,对于每个
反证:假设对于每个
因为
矛盾!故对于每个
\texttt{code}
#include <bits/stdc++.h>
using namespace std;
//&Start
#define lng long long
#define lit long double
#define re register
#define kk(i,n) " \n"[i==n]
const int inf=0x3f3f3f3f;
const lng Inf=0x3f3f3f3f3f3f3f3f;
//&Data
const int N=1e5;
int X,T,n;
//&Solve1
void solve1(){
if((n&1)&&(n^1)) return puts("0"),void();
else {
putchar('2'),putchar(' ');
for(int i=1;i<=n;i++)
printf("%d%c",(i&1)?n+1-i:i-1,kk(i,n));
}
}
//&Solve2
bitset<N+10> np;
int p[N+10],cnt;
void Prime(){
np[1]=true;
for(int i=1;i<=N;i++){
if(!np[i]) p[++cnt]=i;
for(int j=1;j<=cnt&&i*p[j]<=N;j++)
np[i*p[j]]=1;
}
}
int Pow(int a,int x){
int res=1;
for(;x;a=1ll*a*a%n,x>>=1)
if(x&1) res=1ll*res*a%n;
return res;
}
void solve2(){
if(np[n]&&(n^1)&&(n^4)) return puts("0"),void();
else {
if(n==1) return puts("2 1"),void();
if(n==4) return puts("2 1 3 2 4"),void();
putchar('2');
for(int i=1,tmp=1,sum=1;i<=n-1;i++){
printf(" %d",tmp);
tmp=1ll*Pow(sum,n-2)*(i+1)%n;
sum=1ll*sum*tmp%n;
}
printf(" %d",n),putchar('\n');
}
}
//&Main
int main(){
scanf("%d%d",&X,&T);
if(X==2) Prime();
for(int ti=1;ti<=T;ti++){
scanf("%d",&n);
if(X==1) solve1();
else solve2();
}
return 0;
}
祝大家学习愉快!