置换(Leonardo's Notebook,LA 3641)

关于置换群的理论一无所知= =,数学真的好重要。


讲一下置换乘法吧,就只是自己的理解,看了好久才想明白,也不知道对不对。

首先对于置换

(1  2  3  4)

(3  4  1  2)

我们可以拆成循环相乘

(1  3)(2  4)

因为对于上述置换,1->3->1是一个循环,2->4->2是一个循环。相乘就是每个循环都依次执行一次置换。注意,在这里这两个循环都不相交。


如果两个循环相交怎么办呢?


如(1  2  3)(1  2  3)是什么意思呢?大概就是从左往右查表的感觉。(1  2  3)是第一个表,(1  2  3)是第二个表。

比如说1经过这个置换变成了什么?查第一个表(1  2  3),发现1变成了2。再查第二个表(1  2  3),2又变成了3。最终1变成了3。

对于2,查第一个表,2变成了3,。查第二个表,3变成了1.最终2变成了1。

对于3,最终变成了2.。

所以循环相乘的置换就是

(1  2  3)

(3  1  2)

写成循环相乘的形式就是(1  3  2)。

因此(1  2  3)(1  2  3)=(1  3  2)。变成了不相交的循环乘积。

可以证明这个方法具有普适性,(a1  a2  a3)(a1  a2  a3)=(a1  a3  a2)


再如(1  2  3  4)(1  2  3  4)

对于1,查第一个表(1  2  3  4),1变成2,再查第二个表,2变成了3,。最终1变成了3。

对于2,最终变成了4。

对于3,最终变成了1。

对于4,最终变成了2。

那么写成置换的形式就是

(1  2  3  4)

(3  4  1  2)

写成循环相乘的形式就是(1  3)(2  4)。也变成了不相交循环的乘积。

也可以证明这个方法具有普适性,(a1  a2  a3  a4)(a1  a2  a3  a4)=(a1  a3)(a2  a4)。


不难总结出如下规律:

两个长度为n的相同循环相乘,

当n为奇数时结果也是一个长度为奇数n的不相交循环。

当n为偶数时结果就是两个长度为n/2的不相交循环的乘积。


相同奇*相同奇=不交奇

相同偶*相同偶=不交(偶/2)*不交(偶/2)

注意(偶/2)可能为奇也可能为偶。


倒过来拆分的话就是。

一个长度为奇数的不相交循环可以拆成两个长度为奇数的相同循环相乘。

两个长度为奇数的不相交循环可以拆成两个长度为偶数的相同循环相乘。

两个长度为偶数的不相交循环可以拆成两个长度为偶数的相同循环相乘。


我们发现长度为奇数的循环无论如何都可以单独拆分成两个相同循环的乘积。

但是长度为偶数的循环就必须找到另一个长度相等的不相交循环一起,才可以拆成两个相同循环的乘积。


那么对于本题就是给你一个置换问你能不能拆成两个相同置换的乘积。

那就是先把置换变成循环乘积,然后看一下任意偶数长度的循环的个数是否都为偶数。如果可以拆,否则就不可以。

遍历所有循环,计算长度并保存,然后再判断就好了。


计算长度用dfs。

开一个vis数组,访问过的就标记vis,代表这个点已经被之前的某些循环访问过了。以后就不用再进去了。


代码

#include
using namespace std;

char str[30];
int vis[30];
int cnt[30];

int dfs(int& chu,int now,int d)
{
    vis[now]=1;
    if(chu==now) return d;
    else return dfs(chu,str[now]-'A',d+1);
}

int ok()
{
    for(int i=0;i<26;i++)
        if(!vis[i])
            cnt[dfs(i,str[i]-'A',1)]++;
    for(int i=2;i<=26;i+=2)
        if(cnt[i]&1)
            return false;
    return true;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",str);
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        if(ok()) puts("Yes");
        else puts("No");
    }
    return 0;
}


你可能感兴趣的:(大白书-第2章-数学基础,置换)