ICPC NEAU Programming Contest 2020 随便置换
题目大意:
中文题就自己看吧
题解:
这个题目还是很复杂的,挺绕的,想说明白也需要很强的表述能力。
-
首先假设 (a) 表示原始串,(c) 表示目标串,(p) 表示置换串,那么题目中已知的等式关系是 (a=b*p^m) 求 (p)
-
第一步先求 (p^m) 这个应该不太难求
-
直白的来说就是直接把 (p^m) 当作一个置换串 (s) ,已知原串和目标串,求这个中间置换串 (s)
-
完成第一步之后,这个目标串和原始串就没什么太大的意义了,接下来看这个中间置换串 (s)
-
(s) 也就是 (p^m), 这个是一个变化串,他把 (a) 按照本身的变化规律变成了 (c) 串。
-
接下来说说 (s) 串的变化规律:
-
(s[1]=6) 表示原来第1个数变成了6
-
(s[2]=4) 表示原来第2个数变成了4,(s[i]) 表示的原来的第 (i) 个变成了第 (s[i])
-
... 以此类推
-
-
已知 (s) 串的变化规律,那么我要求 (s^2) 这个串,那是不是相当于原来的 (s) 串往后挪了两位,(s[1]) 变成了6之后又变成了3,(s[2]) 变成了4之后又变成了7,所以可以得到 (s^2=3745621) 这个串,这个串的变化规律也很好求,就是原来的串往后挪了两位。
这个图就是上面哪个图两格两格的跳变化出来的图,就是说 (s) 串按照 (s) 串的规律变化一次之后任选一个起点,之后的第 (i) 点是原来的这个点的位置往后跳 (i*2)
-
接下来讨论(p) 的变化规律,已知 (p^m=p*p...*p) ,所以 (p^m) 是原来 (p) 往后跳 (m) 格形成的图。
-
此例题的 (m=2) ,先任意选一个起点,假设起点是1:
-
那么1后面的第 (m) 个格子是6,b[2]=6
b[0] = 1 b[1]=0 b[2]=6 b[3]=0 b[4]=0 b[5]=0 b[6]=0
-
那么6后面的第 2个格子是3,所以第4个 b[4]=3
b[0] = 1 b[1]=0 b[2]=6 b[3]=0 b[4]=3 b[5]=0 b[6]=0
-
以此类推,最后可以得出这个 (p) 的变化规律
b[0] = 1 b[1]=4 b[2]=6 b[3]=7 b[4]=3 b[5]=5 b[6]=2
-
-
这个变化规律图得到之后,就可以求 (p) 这串是什么了,这个的求法和之前从串变成规律图操作差不多。
(p[1]=4) (p[4]=6) ...
以上都是这个题目的整体求解思路,代码中有部分细节讲解。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int a[maxn],f[maxn],cnt[maxn],b[maxn],p[maxn];
int Find(int x){
return x==f[x]?x:f[x]=Find(f[x]);
}
void init(int n){
for(int i=1;i<=n;i++) f[i]=i,cnt[i]=0;
}
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
init(n);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
a[x]=i;
if(Find(i)!=Find(x)){
f[i]=x;
/*
* 这一步再为求环上的节点数量做准备
* 也可以用dfs求环上的节点数量
* 但是这些都是简单环,所以用并查集比较简单
*/
}
}
for(int i=1;i<=n;i++) cnt[Find(i)]++;
/*
* a就是p^m这个串
* cnt[i]表示以i点为根节点的这个环的节点数量
* 接下来求判断是否有解,因为题目可能不止一个环,所以要判断每一个环的大小是不是都是与m互质
* 如果不互质,那么不可能找到答案,因为不互质,那么总会有一些点找不到
*/
int flag = 1;
for(int i=1;i<=n;i++){
if(!cnt[i]) continue;
if(gcd(cnt[i],m)!=1){
flag = 0;
break;
}
}
if(!flag){
printf("NO
");
continue;
}
/*
* 判断有解之后就开始求p这个串的变化规律了
*/
// for(int i=1;i<=n;i++) printf("a[%d]=%d
",i,a[i]);
for(int i=1;i<=n;i++){
if(!cnt[i]) continue;
int l = cnt[i], w = i;
for(int j=0;j<l;j++){
b[j*1ll*m%l] = w;
w=a[w];
// printf("b[%d]=%d
",j*m%l,b[j*1ll*m%l]);
}
for(int j=0;j<l;j++){
p[b[j]]=b[(j+1)%l];
}
/*
* 这里是在求p串,w首先等于i,表示将i定为起点
* 所以b[0]=w
* 从这个点往后跳m格b[m]=a[w]
* 得到b串之后再转化为p串即可
* !!!注意这个位置p[b[j]]=b[(j+1)%l]
* 这是因为我存的这个变化串是 a[i] 表示的是原来的第i个变成了第a[i]个
* 。。。。好像没有讲清楚,自己思考吧,差不多就是怎么变成图的就怎么变回来
* 假设 原来是 x1->x2 a[x2]=x1
* 那么之后的 x1->x2 p[x1]=x2
*/
}
printf("YES
");
for(int i=1;i<n;i++) printf("%d ",p[i]);
printf("%d
",p[n]);
}
}