1 条题解
-
0
自动搬运
来自洛谷,原作者为

George1123
**搬运于
2025-08-24 21:50:35,当前版本为作者最后更新于2020-03-15 13:20:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题解-Koishi Loves Construction
前缀知识
质数 逆元 暴搜
给定 , 组测试数据,每次给一个 。
- 如果 ,构造一个 的排列使得前缀和模 互不相同。
- 如果 ,构造一个 的排列使得前缀积模 互不相同。
数据范围:,,。
属于“思维体操”,做之前会异常兴奋,做之后会只想睡觉。
设序列为 ,前缀和/积为 。
分类讨论:
初步发现:
-
不能有区间 和是 的倍数,否则 。
-
设 ,必须 ,否则 。
-
,因为如果 :
然后由此判断输出 ,交了一发, 分——很明显判断对了。
于是开始打暴力:
#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得出规律:
- 如果 ,。
- 如果 ,。
//&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很明显:
- 如果 ,。
- 如果 ,。
最后在模 意义下还原成正数,
初步发现:
- 不能有区间 的积 。
- 不能有区间 的积 。
- 。
- 。
- 还有如果 也不行,很明显。
然后由此判断输出 ,交了一发, 分——很明显判断对了
至于序列长什么样,暴力再来一发:
#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! ***/进一步推测:如果 是质数或者 或者 ,可以构造。
由此判断输出 提交一发, 分,很明显对了(然而没什么用啊。
然后我下了一下数据,竟然发现输出数据只有 。
再进一步发现:第二个数只能是 ,没用。
这时看输出(我看了 分钟)
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'); } }提交了一发, 了。
证明:
很明显,对于每个 , 是唯一的,只需证明:对于每个 , 是唯一的。
反证:假设对于每个 , 不唯一,设 。
因为 ,所以必定有 。
**矛盾!**故对于每个 , 是唯一的。
#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; }
祝大家学习愉快!
- 1
信息
- ID
- 2671
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者