置换(群论)

 最近研究了一下置换:
不感兴趣者可直接看结论,由于以下证明都是自己想出来的,转载请说明出处。

性质1:对于长度为n的置换T,T^k的循环个数为gcd(n,k)


因为当k|n,T可以分解成k个循环,每个循环都是k的同余类

gcd(n,k)=1,T不能分解,因为k*s%n=k*t%n等价于n|k(s-t),由此可以肯定n|(s-t),s!=t的情况下,s=n*h+t>n,也就是要形成循环必须跳n步以上,也就是循环的长度至少为n,而最大也只能为n,因此不能分解

其他情况k=gcd(n,k)*(k/gcd(n,k)),等价计算(T^gcd(n,k))^(k/gcd(n,k)),由于(k/gcd(n,k))|n,所以T^ gcd(n,k)分解成gcd(n,k)个循环,并且每个循环的长度为n/gcd(n,k),计算(T^ gcd(n,k))^(k/gcd(n,k))时可以独立计算T^ gcd(n,k)的每个循环的k/gcd(n,k)次幂,由于gcd(k/gcd(n,k), n/gcd(n,k))=1,那么每个循环均不能分解,最终就是gcd(n,k)个循环

 

性质2:对于长度为n的置换T,T^k为恒等置换的最小正整数kn

因为T错位为1,T^2错位为2,……,T^(n-1)错位为n-1,T^n错位为0

推论:对于含有若干循环的置换T, T^k为恒等置换的最小正整数k为所有循环的长度的最小公倍数


poj3270  使一个序列有序化的最小代价,考虑序列循环内部采用最小数交换,或者用整个序列最小值替换循环内最小值,然后进行循环内最小数交换,最后再把原先循环内交换出去的数交换回来。

具体参考这篇博客:http://www.cnblogs.com/DreamUp/archive/2010/08/17/1801700.html 

类似题目求最小交换次数 uva11077

当然此题求方案数,自然使用dp,具体见大白书。

#include<cstdio>
#include<iostream>
#include<cstring>
#define ull unsigned long long
using namespace std;

ull dp[30][30];
int main()
{
    int n,k;
    while(cin>>n>>k,n){
        memset(dp,0,sizeof dp);
        for(int i=1;i<=n;i++) dp[i][0]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<i;j++)
                dp[i][j]+=dp[i-1][j]+(i-1)*dp[i-1][j-1];
        cout<<dp[n][k]<<endl;
    }
	return 0;
}

poj1026 置换的k次幂 重载置换乘法,然后快速幂即可,其实可以做到线性,从循环角度考虑。

poj2369 置换群至少经过多少次幂后,可变成恒等置换,答案是所有循环长度的最小公倍数。

poj1721 置换开方,详见上面的博客链接。

poj3128 是否存在置换T,满足T^2等于给定的置换,首先对于长度为奇数的循环,乘幂为2,根据上面的性质1,循环不分裂,反过来说长度为奇数的循环开2次方不合并,并且根据一对一的关系,解总是存在的。对于长度为偶数的循环,乘幂为2,则会分裂成2个不相交的循环,而且是2的同余类,因此将两个偶循环交替组合即可合并。因此只要满足偶循环个数为偶数,则必然有解,否则无解。

poj3590 将n拆成若干个正整数,使lcm最大,这里可以使用dp,并且可以证明最优解必然拆成素因子的次幂形式。

可以这么看,若存在两个数不互质,那么其中一个数除掉gcd,不改变总的lcm,但使和减小,反过来说,将这部分减少的再重新分配下可能得到更优解。因此分解的数两两互质,再考虑另一种情况,其中一个数为a^p1*b^p2...的形式,其中a和b为质数,这显然不是最优,将这个数拆成a^p1+b^p2+...,同样不改变总的lcm,但又使和减小,也就是有可能获得更优解。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;

int cnt[110],res[110],dp[110][110];
int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b){
    return a/gcd(a,b)*b;
}
vector<int> dv;
int main()
{
    int t,n;
    cin>>t;
    while(t--){
        cin>>n;
        memset(dp,0,sizeof dp);
        for(int i=1;i<=n;i++) dp[i][1]=i;
        for(int i=2;i<=n;i++)
            for(int j=2;j<=i;j++)
                for(int k=j-1;k<i;k++){
                    dp[i][j]=max(dp[i][j],lcm(dp[k][j-1],i-k));
                }
        int ans=0;
        for(int i=1;i<=n;i++) ans=max(ans,dp[n][i]);
        memset(cnt,0,sizeof cnt);
        int m=ans,sum=0,c=1;
        for(int i=2;i*i<=m;i++){
            if(m%i==0){
                while(m%i==0){
                    c*=i;
                    m/=i;
                }
                cnt[c]++;
                sum+=c;
                c=1;
            }
        }
        cnt[m]++,sum+=m;
        cnt[1]+=n-sum;
        cout<<ans;
        dv.clear();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=cnt[i];j++)
                dv.push_back(i);
        int k=1;
        for(int i=0;i<dv.size();i++)
            if(dv[i]==1) res[k]=k,k++;
            else{
                for(int j=k;j<k+dv[i]-1;j++)
                    res[j]=j+1;
                res[k+dv[i]-1]=k;
                k+=dv[i];
            }
        for(int i=1;i<=n;i++)
            cout<<" "<<res[i];
        cout<<endl;
     }
	return 0;
}


Polya定理

实际上是求每个置换的循环数,对每个置换,累加一个颜色数^循环数。这个关键是如何求每个置换的循环数,简单的手推即可,或者程序跑一个全排列。

hdu1812 这题初看不怎么好求循环数,我们来看一下旋转90度的情况,可以看到正方形的四个顶点转了一圈,也就是形成了一个循环节为4的循环,然后你可以在正方形中找到每个这样的循环节为4的循环,注意当n为奇数时,最中间的一个是不动的,单独形成一个循环。然后其他旋转类似,轴对称就比较简单。由于这题要爆long long,所以用java写吧。

import java.math.BigInteger;
import java.util.Scanner;


public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int n,c;
		Scanner cin = new Scanner(System.in);
		while(cin.hasNextInt()){
			n = cin.nextInt();
			c = cin.nextInt();
			BigInteger base = BigInteger.valueOf(c);
			BigInteger ans = BigInteger.ZERO;
			if((n&1)==0){
				ans = ans.add(base.pow(n*n/4).multiply(BigInteger.valueOf(2)));
				ans = ans.add(base.pow(n*n));
				ans = ans.add(base.pow(n*n/2).multiply(BigInteger.valueOf(3)));
				ans = ans.add(base.pow((n*n-n)/2+n).multiply(BigInteger.valueOf(2)));
			}
			else{
				ans = ans.add(base.pow(n*n/4+1).multiply(BigInteger.valueOf(2)));
				ans = ans.add(base.pow(n*n));
				ans = ans.add(base.pow(n*n/2+1));
				ans = ans.add(base.pow((n*n-n)/2+n).multiply(BigInteger.valueOf(4)));
			}
			System.out.println(ans.divide(BigInteger.valueOf(8)));
		}
	}

}
未完...


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值