置换(群)&(J Just Shuffle)

牛客上多校第二场的J题,下来补的时候发现要置换群,于是就来学了,然后就想着记个笔记,这里记几篇资源。
https://wenku.baidu.com/view/7b373e6b561252d380eb6ee5.html
https://blog.csdn.net/weixin_44412226/article/details/107395789

置换(群)
置换

先说置换是啥
对于一个n个元素集合 S = { a 1 , a 2 . . . a n } S = \{a_1,a_2...a_n\} S={a1,a2...an},排列 P = { p 1 , p 2 . . . p n } P = \{p_1,p_2...p_n\} P={p1,p2...pn},将排列P每个元素当做S的下标替换掉原本S每个元素的位置,就得到一个置换 f = { a p 1 , a p 2 . . . a p n } f = \{a_{p_1},a_{p_2}...a_{p_n}\} f={ap1,ap2...apn}
可以写作
f = ( a 1 , a 2 , . . . a n a p 1 , a p 2 . . . a p n ) f=\left( \begin{array}{c} {a_1,a_2,...a_n}\\ {a_{p_1},a_{p_2}...a_{p_n}} \end{array} \right) f=(a1,a2,...anap1,ap2...apn)
由于排列有 n ! n! n!种,所以置换也会有 n ! n! n!

置换的环

有一个排列为 A = ( 2 , 3 , 1 , 5 , 4 ) A= (2,3,1,5,4) A=(2,3,1,5,4)
还有一个排列为 P = ( 3 , 1 , 2 , 5 , 4 ) P=(3,1,2,5,4) P=(3,1,2,5,4),将A通过P进行置换可以得到:

  1. 置换第一次: ( 1 , 2 , 3 , 4 , 5 ) (1,2,3,4,5) (1,2,3,4,5)
  2. 置换第二次: ( 3 , 1 , 2 , 5 , 4 ) (3,1,2,5,4) (3,1,2,5,4)
  3. 置换第三次: ( 2 , 3 , 1 , 4 , 5 ) (2,3,1,4,5) (2,3,1,4,5)
  4. 置换第四次: ( 1 , 2 , 3 , 5 , 4 ) (1,2,3,5,4) (1,2,3,5,4)

从上面的结果就足以看出规律来了。

  1. 置换是可以分成块的:也就是(2,3,1)和(4,5)这两个块,你可以发现无论怎么置换都不会发生这两个集合的元素之间互换。
  2. 存在循环:也就是对于每个块,在置换一定次数之后就会变回原样,比如(2,3,1)这个块在置换三次后就会变回原来排列中的位置
  3. 每个环的(下面不叫块了叫环)循环的大小就是块的元素的数目

对于上面的置换,有两个环(1,2,3)和(4,5),大小为3和2,那么上述的置换可以拆成这两个环,。

可以证明所有的置换都可以拆成若干个环 w 1 , w 2 . . . w n w_1,w_2...w_n w1,w2...wn, 而每个环的大小为 r 1 , r 2 . . . r n r_1,r_2...r_n r1,r2...rn,也就是说对于环 w i w_i wi在对原排列置换 r i r_i ri次后保持不变,那么也就可以得到原排列置换 l c m ( r 1 , r 2 . . . r n ) lcm(r_1,r_2...r_n) lcm(r1,r2...rn)次后保持不变。

一些关系

对于排列A,使用置换P置换K次后得到B,记为 A k = B A^k = B Ak=B,如果A的所有环的最小公倍数大小为C,那么 A C = A A^C = A AC=A

若:
A A A A 1 A^1 A1使用的置换是P
A A A A 2 A^2 A2则是把置换P向后转动一次
A A A A 3 A^3 A3则是转动P两次

比如对于排列A = (2,3,1),置换P = (3,1,2)
A 1 A^1 A1(相当于置换一次从A到 A 1 A^1 A1)为(1,2,3)
A 2 A^2 A2 = (3,1,2)
A 3 A^3 A3 = (2,3,1)
把从A到 A 2 A^2 A2的置换记做P’,那么可以得到P’ = (2,3,1)与P相比就相当于把P的所有元素向右转了一下。
根据上面关系可以得到在P置换下的 A 2 g A^{2g} A2g = 在P转动一次下置换下的 A g A^{g} Ag
注意:
上述关系的成立前提是k与A的环的大小互质

J Just Shuffle

这道题让求一个置换P,给出指定的排列 B = ( 1 , 2 , 3 , . . , n ) B = (1,2,3,..,n) B=(1,2,3,..,n),和排列 A A A,和一个整数K,使在P置换下 B k = A B^k = A Bk=A

首先可以很简单的求出一个置换 P 0 P_0 P0满足 B 1 = A B^1 = A B1=A,但是题要求是 B K = A B^K = A BK=A所以还需要在变化,由于置换是有环的,假设 P 0 P_0 P0有若干个环, R 1 , R 2 . . . R n R_1,R_2...R_n R1,R2...Rn大小为 r 1 , r 2 , . . . r n r_1,r_2,...r_n r1,r2,...rn那么对于第i个环有
R i k = R i k % r i R_i^k = R_i^{k \% r_i} Rik=Rik%ri,由于置换转一下就增一倍,所以置换转 i n v inv inv下时相对于原来就转了 k ∗ i n v k*inv kinv下,而最终我们的目的是是让他等于1,所以就有 k ∗ i n v = 1 ( m o d r i ) k*inv = 1(mod r_i) kinv=1(modri)成立即inv为k的逆元,也就是说当原本的环的置换转inv下时就是答案了。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define pb push_back
#define eps 1e-8
#define endl '\n'
using namespace std;
const ll maxn = 1e6 + 5;
int n,k,a[maxn],ans[maxn];
bool vis[maxn];
vector<int> v;
void setSubstitution(){
    int r = v.size(),inv;//r为环的大小,inv为k在r下的逆元
    for(int i = 0; i < r; i++)
        if((ll) k * i % r == 1)inv = i;//求逆元
    for(int i = 0; i < r; i++)
        ans[v[i]] = v[(i + inv) % r];//转inv次
}
int main(){
    scanf("%d %d",&n,&k);
    for(int i = 1; i <= n; i++)
        scanf("%d",a + i);
    for(int i = 1; i <= n; i++){
        if(!vis[i]){
            v.clear();
            int x = a[i];
            while(!vis[x]){//找环操作
                v.push_back(x);
                vis[x] = 1;
                x = a[x];
            }
            setSubstitution();
        }
    }
    for(int i = 1; i <= n; i++)
        printf("%d ",ans[i]);
    return 0;
}
### Cache存储器与虚拟存储器的工作原理 #### Cache存储器概述 Cache是一种高速缓存技术,位于CPU和主存之间。为了提高访问速度,主存中的数据被划分为固定大小的块(即主存块),而Cache也被划分成同样大小的块(即Cache块)。当CPU需要从内存中读取或写入数据时,会先检查所需的数据是否已经在Cache中存在[^2]。 如果在Cache中找到了对应的数据,则称为命中;如果没有找到,则会发生一次缺失,这时就需要从主存加载相应的块到Cache中,并可能替换掉某些已有的Cache块。这种机制能够显著减少平均访存时间,因为大多数情况下程序具有局部性特征,使得最近使用的数据更有可能再次被使用。 #### 虚拟存储器概述 虚拟存储器通过引入逻辑地址空间来扩展物理内存容量。段页式虚拟存储器结合了分段和分页两种方式的优点,在支持按段进行共享的同时也能提供较好的内存管理和保护功能。然而,由于每次地址转换都需要经历两次查找过程(首先是段表,其次是页表),这增加了系统的额外开销[^1]。 对于操作系统而言,它负责管理这些复杂的映射关系以及处理页面置换等问题。应用程序员通常不需要关心具体的细节,只需要知道自己的程序可以在更大的地址范围内运行即可。 #### 主要区别 | 特征 | Cache 存储器 | 虚拟存储器 | | -- | --- | | **位置** | CPU 和 RAM 间的小型临时缓冲区 | 系统级抽象层,涉及磁盘交换文件作为辅助存储 | | **目的** | 提升频繁访问数据的速度 | 扩展可用内存总量,允许更大规模的应用执行 | | **透明度** | 对程序员完全透明 | 需要操作系统的配合和支持 | | **硬件/软件依赖** | 完全由硬件实现 | 结合软硬件共同完成 | ### 示例代码展示两者差异 下面是一个简单的Python模拟例子用于说明两者的不同之处: ```python import random class SimpleCache: def __init__(self, size=8): self.cache = {} self.size = size def access(self, address): if address not in self.cache or len(self.cache) &gt;= self.size: print(f&quot;Miss! Loading data from main memory at {address}&quot;) # Simulate loading into cache (here we just add it to dict) self.evict_if_full() self.cache[address] = &quot;data&quot; else: print(f&quot;Hit! Data found in cache for {address}&quot;) def evict_if_full(self): if len(self.cache) &gt;= self.size: key_to_remove = next(iter(self.cache)) del self.cache[key_to_remove] def virtual_memory_simulation(): addresses = list(range(0, 100)) * 5 # Repeat some addresses multiple times. random.shuffle(addresses) simple_cache = SimpleCache() for addr in addresses[:20]: simple_cache.access(addr) if __name__ == &quot;__main__&quot;: virtual_memory_simulation() ``` 此脚本创建了一个简易版的Cache模型`SimpleCache`类,并展示了如何模拟Cache命中的情况。而对于虚拟存储器来说,实际应用中涉及到的操作更为复杂,包括但不限于页面调度算法的选择、异常处理等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值