【浅谈】置换群

前言

本博客偏 math

Valera and Swaps

构造一个操作序列,每次 swap(p[i],p[j]) ,使得最终序列经过最少 k 次操作后能使每个 p[i]=i 。输出字典序最小。

结论1. 连边 i->p[i],则 操作数=n-环的个数

正确性显然。

分类讨论:

  1. cnt<k 则输出 k-cnt ,有如下合并环操作:选取 pos[i]!=pos[j] ,执行 swap(p[i],p[j])
  2. cnt>k 则输出 cnt-k ,有如下拆分环操作:选取 pos[i]==pos[j],i!=j ,执行 swap(p[i],p[j])
    请添加图片描述
    方便起见,对于一个环,我们把首位打一个标记。

时间复杂度 O(n^2)

#include<bits/stdc++.h> #define ll long long const int mx=3005; using namespace std; int n,m,k,cnt,cnt2,p[mx],prt[mx],pos[mx],cord[mx]; vector<int> vec[mx]; //方便起见,对于一个环,我们把首位打一个标记。 int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&p[i]); scanf("%d",&k); //所需次数 = n - 环的个数 for(int i=1;i<=n;i++) { if(pos[i]) continue; int j=i; m=0; cnt++; cord[j]=1; //首位打标记 while(!pos[j]) prt[m++]=j,pos[j]=cnt,j=p[j]; } cnt=n-cnt; //细节问题 if(k<cnt) { printf("%d\n",cnt-k); // cout<<"YES"<<endl; for(int i=1;i<=cnt-k;i++) { for(int j=1;j<=n;j++) vec[j].clear(),pos[j]=0; cnt2=0; for(int j=1;j<=n;j++) { if(cord[j]) { cnt2++; for(int k=j;!pos[k];k=p[k]) pos[k]=cnt2; } } for(int j=1;j<=n;j++) { vec[pos[j]].push_back(j); // cout<<pos[j]<<" "; } // cout<<endl; for(int j=1;j<=n;j++) cord[j]=0; //重新标号 //相同环分离 for(int j=1;j<=n;j++) { if(vec[pos[j]].size()>1) { int k=vec[pos[j]][1]; swap(p[j],p[k]); cord[j]=cord[k]=1; vec[pos[j]].clear(); printf("%d %d ",j,k); break; } } for(int j=1;j<=n;j++) { if(vec[j].size()) { cord[vec[j][0]]=1; vec[j].clear(); } } } } else if(k==cnt) { printf("0"); } else { printf("%d\n",k-cnt); //不同环合并 for(int i=1;i<=k-cnt;i++) { for(int j=1;j<=n;j++) vec[j].clear(),pos[j]=0; cnt2=0; for(int j=1;j<=n;j++) { if(cord[j]) { cnt2++; for(int k=j;!pos[k];k=p[k]) pos[k]=cnt2; } } for(int j=2;j<=n;j++) { //不在同一个环 if(pos[1]!=pos[j]) { cord[pos[j]]=0; // swap(p[1],p[j]); printf("1 %d ",j); cord[1]=1; break; } } } } }

[NOIP2005 提高组] 篝火晚会

题意看错了,以为只能交换连续的 m 个位置。。。

说白了你有两个环,问你经过最少几次操作后一模一样。

显然 a->bb->a 等价,假设样例给的环是这样:
请添加图片描述
显然也可以逆时针构造,所以要跑两次:
请添加图片描述
环上问题考虑破成 2n 链: 1 4 2 3 1 4 2 3

结论1: 最小操作次数 n-k ,其中 k 表示相对位相同的个数。

举例 1 4 2 3 , k=1 ; 1 3 2 4 , k=2 。所以样例答案为 2

暴力枚举初始点 i ,然后暴力匹配,时间复杂度 O(n^2)

显然可以用桶优化,如下图:

请添加图片描述
时间复杂度 O(n) 。显然吧 qwq

#include <bits/stdc++.h> #define INF 0x3f3f3f3f #define ll long long using namespace std; const int mx = 1e5 + 5; int n, m, res(INF), prt[mx], l[mx], r[mx], dig[mx], pos[mx], a[mx], b[mx], c[mx], tong[mx]; void dfs(int x, int nxt) { prt[++m] = x; pos[x] = 1; if (pos[nxt]) return; dfs(nxt, (x == l[nxt]) ? r[nxt] : l[nxt]); } //似乎答案就是逆序对 void solve() { // for(int i=1;i<=n;i++) printf("%d ",prt[i]); // printf("\n"); memset(tong,0,sizeof(tong)); for(int j = 1; j <= n; j ++) { tong[(j-prt[j]+n)%n]++; } for(int j = 0; j < n; j++) res=min(res,n-tong[j]); } int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) a[i] = a[i+n] = i; for (int i = 1; i <= n; i++) { scanf("%d%d", &l[i], &r[i]); } for (int i = 1; i <= n; i++) { if (l[l[i]] != i && r[l[i]] != i || l[r[i]] != i && r[r[i]] != i) { printf("-1"); return 0; } } //向左走 dfs(1, l[1]); solve(); m = 0; memset(pos, 0, sizeof(pos)); //向右走 dfs(1, r[1]); solve(); printf("%d", res); }

Moodular Arithmetic

首先观察,样例 9=3*325=5*5 ,猜测答案就是 n^k

再仔细观察,发现 k 就是环的个数。但是要特判 k=1 的情况,此时答案就是 n^n

至于连边,你把 f 函数的自变量 x->k*x%p 连边,很容易看出是完全剩余系吧。

#include <bits/stdc++.h> #define INF 0x3f3f3f3f #define ll long long using namespace std; const int mx = 1e5 + 5; int n, m, res(INF), prt[mx], l[mx], r[mx], dig[mx], pos[mx], a[mx], b[mx], c[mx], tong[mx]; void dfs(int x, int nxt) { prt[++m] = x; pos[x] = 1; if (pos[nxt]) return; dfs(nxt, (x == l[nxt]) ? r[nxt] : l[nxt]); } //似乎答案就是逆序对 void solve() { // for(int i=1;i<=n;i++) printf("%d ",prt[i]); // printf("\n"); memset(tong,0,sizeof(tong)); for(int j = 1; j <= n; j ++) { tong[(j-prt[j]+n)%n]++; } for(int j = 0; j < n; j++) res=min(res,n-tong[j]); } int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) a[i] = a[i+n] = i; for (int i = 1; i <= n; i++) { scanf("%d%d", &l[i], &r[i]); } for (int i = 1; i <= n; i++) { if (l[l[i]] != i && r[l[i]] != i || l[r[i]] != i && r[r[i]] != i) { printf("-1"); return 0; } } //向左走 dfs(1, l[1]); solve(); m = 0; memset(pos, 0, sizeof(pos)); //向右走 dfs(1, r[1]); solve(); printf("%d", res); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530319.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(21)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示