性质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为恒等置换的最小正整数k为n
因为T错位为1,T^2错位为2,……,T^(n-1)错位为n-1,T^n错位为0
推论:对于含有若干循环的置换T, T^k为恒等置换的最小正整数k为所有循环的长度的最小公倍数
具体参考这篇博客: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)));
}
}
}
未完...