等价类计数问题,置换,Burnside,Polya

这几天学习了一下Burnside,而Polya只是Burnside的一个特例。 

Burnside可以用下面这个公式概括: 

本质不同的方案数  =   所有置换的不动点数之和  /  置换的个数

什么是不动点?  

如果对一个染色方案执行一次置换F后该方案保持不变,则该置换是F的一个不动点。 

常见的置换方法有旋转、翻转。

考虑一个圆环:

可以绕n条对称轴翻转(不论n是奇还是偶) , 可以绕中心旋转0,1,2,3...n-1位 ,故共有2*n种置换。

怎样求对于"旋转i位" 这个置换 的不动点数 ?

每个循环的长度 l = lcm(i,n) / i = n / gcd(i , n) 

循环的个数       g = gcd(i , n) 

其等效模型为  “长为g的一个圆环不考虑旋转时的染色方案数” ,此时不加限制的方案数为c^g 。 

当n很大时显然不能枚举i ,这时需要按g值对i进行分类 , 设f(g)为 gcd(i , n) = g的不同i的个数 ,即gcd(i/g, n/g) = 1的不同i的个数 ,显然 f(g) = phi(n/g) . [欧拉函数]

因此可以通过枚举n的因子g来确定答案。 

于是这类“圆环旋转问题”的等价类计数问题可以用这样一个模板解决:

    ①枚举n的所有因子g

    ② sum += fixpoint(g) * phi(n /g) 

    ③ ans = sum / 置换的个数.

其中fixpoint(g) 就是  “长为g的一个圆环不考虑旋转时的染色方案数” 这个问题的答案。 

而通常问题的难点也是fixpoint(g)的计算,题目一般会给出一些限制。

可能是dp 、矩阵快速幂 、 数学推导的综合体 。 。。 


还是通过几个例题来加深理解吧。。

http://vjudge.net/contest/view.action?cid=47722#overview

最后两个神题,实在感到力不从心,希望大神们指点一二。。

POJ 1286 Necklace of Beads

直接套Burnside即可。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
LL pow3[25] , ans[25] ;
int gcd(int a,int b) {
    return b==0 ? a : gcd(b , a%b) ;
}

LL f(int n) {
    LL sum = 0 ;
    for(int i=0; i<n; i++) sum += pow3[gcd(i ,n)];
    if(n&1) sum += (LL)n * pow3[(n+1)/2] ;
    else sum += (LL)n/2*pow3[n/2+1] + (LL)n/2*pow3[n/2] ;
    return sum / (n*2) ;
}

int main()
{
    pow3[0] = 1 ;
    for(int i=1; i<25; i++) pow3[i] = pow3[i-1] * 3;
    for(int i=1; i<25; i++) ans[i] = f(i) ;
    int n ;
    while(scanf("%d" , &n) , ~n) {
        printf("%I64d\n" , ans[n]) ;
    }
    return 0;
}


POJ 2409 Let it Bead

入门基础。 

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
LL pow3[25] , ans[25] ;
int gcd(int a,int b) {
    return b==0 ? a : gcd(b , a%b) ;
}

LL f(int n) {
    LL sum = 0 ;
    for(int i=0; i<n; i++) sum += pow3[gcd(i ,n)];
    if(n&1) sum += (LL)n * pow3[(n+1)/2] ;
    else sum += (LL)n/2*pow3[n/2+1] + (LL)n/2*pow3[n/2] ;
    return sum / (n*2) ;
}

int main()
{
    pow3[0] = 1 ;
    for(int i=1; i<25; i++) pow3[i] = pow3[i-1] * 3;
    for(int i=1; i<25; i++) ans[i] = f(i) ;
    int n ;
    while(scanf("%d" , &n) , ~n) {
        printf("%I64d\n" , ans[n]) ;
    }
    return 0;
}


HDU 1812 Count the Tetris

置换有: 

旋转0°、90°、180°、270° 。

 按n的奇偶考虑对称轴。

import java.io.*;
import java.util.*; 
import java.math.*;

public class Main {
	public static void main(String args[]) {
		Scanner cin = new Scanner(System.in) ; 
		while(cin.hasNext()) {
			int n = cin.nextInt() ;
			BigInteger c = cin.nextBigInteger() ;
			BigInteger sum = BigInteger.ZERO ; 
			if(n%2==1) {
				// 不动
				sum = sum.add(c.pow(n * n)); 
				// 转90°
				sum = sum.add(c.pow((n*n-1)/4 + 1));
				// 转180°
				sum = sum.add(c.pow((n*n-1)/2 + 1)) ;
				// 转 270°
				sum = sum.add(c.pow((n*n-1)/4 + 1)) ;
				// 翻
				sum = sum.add( c.pow((n*n-n)/2+n).multiply(BigInteger.valueOf(4)) ) ;
			}
			else {
				sum = sum.add(c.pow(n*n)) ; 
				
				sum = sum.add(c.pow(n*n/4)).add(c.pow(n*n/2)).add(c.pow(n*n/4)) ;
				
				sum = sum.add((c.pow((n*n - n)/2 + n).add(c.pow(n*n/2))).multiply(BigInteger.valueOf(2))) ;
			}
			BigInteger ans = sum.divide(BigInteger.valueOf(8)) ;
			System.out.println(ans.toString()) ;
		}
		cin.close();
	}
}


POJ 2154 Color

n很大,用欧拉函数优化。 注意n和p不一定互质,不存在n的逆 ,怎么解决呢,前面少乘一次n就行了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long LL ;
const LL maxn = 32000 ;

int pri[maxn/4] , tot ;
bool flag[maxn+1] ;

void init_prime(){
    for(int i=2; i*i<=maxn; i++) if(!flag[i]) {
        for(LL j=i*i; j<=maxn; j+=i) flag[j] = true;
    }
    for(int i=2; i<=maxn; i++) if(!flag[i]) pri[tot++] = i ;
}

LL pow_mod(LL a,LL n ,LL mod) {
    LL ret = 1;
    while(n) {
        if(n&1) ret = ret*a%mod ;
        a = a*a%mod ;
        n>>=1 ;
    }
    return ret ;
}
LL phi(LL n){
    LL ret = n ;
    for(int i=0 ; pri[i]*pri[i]<=n ; i++) if(n % pri[i]==0) {
        ret = ret / pri[i] * (pri[i]-1) ;
        while(n%pri[i] == 0) n /= pri[i] ;
    }
    if(n>1) ret = ret / n * (n-1) ;
    return ret ;
}

int N , P ;
LL ans ;
int fac[100],cnt[100],cn ;
void dfs(int g , int d) {
    if(d == cn) {
        ans += pow_mod(N , g-1 , P) * phi(N / g) ;
        ans %= P ;
        return ;
    }
    int tmp = 1 ;
    for(int i=0; i<=cnt[d] ;i++) {
        dfs(g*tmp , d+1) ;
        tmp *= fac[d] ;
    }
}

int F(LL n , LL p){
    cn = 0 ;
    for(int i=0; pri[i]*pri[i]<=n ;i++) if(n%pri[i] == 0) {
        fac[cn] = pri[i] ;
        cnt[cn] = 0 ;
        while(n % pri[i] == 0) {
            cnt[cn]++ ;
            n /= pri[i] ;
        }
        cn++ ;
    }
    if(n>1) fac[cn] = n  , cnt[cn++] = 1 ;
    ans = 0 ;
    dfs(1, 0) ;
    return ans ;
}

int main()
{
    init_prime() ;
    int T;
    scanf("%d" , &T);
    while(T--){
        scanf("%d%d" , &N , &P) ;
        printf("%d\n" , F(N , P)) ;
    }
    return 0;
}


POJ 2888 Magic Bracelet

同同样n很大。 用矩阵处理限制关系。

// 一篇不错的博客: http://hi.baidu.com/billdu/item/62319f2554c7cac9a5275a0d

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long LL ;
const LL MOD = 9973 ;
const LL maxn = 31650 ;

LL M ;
LL pri[maxn/4] , pri_tot ;
bool _flag[maxn+1] ;
void init_prime(){
    for(LL i=2; i*i<=maxn; i++) if(!_flag[i]) {
        for(LL j=i*i; j<=maxn; j+=i) _flag[j] = true ;
    }
    for(LL i=2; i<=maxn; i++) if(!_flag[i]) pri[pri_tot++] = i;
}

vector<LL>fac;
struct Function{ // 用于计算n的因子,保存在 vector<LL> fac
    LL a[100], p[100], tot ;
    void FenJie(LL n) {
        tot = 0 ;
        for(LL i=0; pri[i]*pri[i]<=n; i++) if(n % pri[i] == 0) {
            p[tot] = 0 ;
            a[tot] = pri[i] ;
            while(n % pri[i] ==0)  n/=pri[i] , p[tot]++ ;
            tot++ ;
        }
        if(n > 1) a[tot] = n , p[tot++] = 1  ;
    }
    void dfs(LL z , LL d) {
        if(d == tot) {
            fac.push_back(z) ;
            return ;
        }
        LL ap = 1 ;
        for(LL i=0; i<=p[d]; i++) {
            dfs(z * ap , d+1) ;
            ap *= a[d] ;
        }
    }
    void  get_fac(LL n) {
        fac.clear() ;
        FenJie(n) ;
        dfs(1 , 0) ;
    }
}sol;

struct Mat{
    LL a[10][10] ;
    Mat(LL x=0){
        memset(a , 0 ,sizeof(a)) ;
        for(LL i=0; i<M; i++) a[i][i] = x;
    }
    Mat operator * (const Mat &b)const {
        Mat ret ;
        for(LL i=0; i<M ;i++){
            for(LL j=0; j<M; j++){
                LL tmp = 0 ;
                for(LL k=0; k<M; k++){
                    tmp += a[i][k] * b.a[k][j] ;
                }
                ret.a[i][j] = tmp % MOD ;
            }
        }
        return ret ;
    }
    Mat operator ^ (LL n) {
        Mat ret(1) ;
        while(n)  {
            if(n&1) ret = ret**this ;
            *this = *this**this ;
            n>>=1 ;
        }
        return ret;
    }
};


LL Pow(LL a , LL n) {
    LL ret = 1 ;
    a %= MOD ;
    while(n) {
        if(n&1) ret = ret * a % MOD ;
        a = a * a % MOD ;
        n>>=1 ;
    }
    return ret ;
}
LL inv(LL x) {
    return Pow(x , MOD-2) ;
}
LL phi(LL n) {
    LL ret = n;
    for(LL i=0; pri[i]*pri[i]<=n; i++) if(n % pri[i] == 0) {
        ret = ret / pri[i] * (pri[i] - 1) ;
        while(n % pri[i] == 0) n /= pri[i] ;
    }
    if(n > 1) ret = ret / n * (n-1) ;
    return ret ;
}

Mat ok ;
LL fixpoLL(LL n) { // 不考虑旋转,长度为n时 的方案数。
    LL ret = 0 ;
    Mat A = ok ;
    A = A ^ n ;
    for(LL i=0; i<M; i++)
        ret += A.a[i][i] ;
    ret %= MOD ;
    return ret ;
}

LL solve(LL n) {
    LL ret = 0 ;
    sol.get_fac(n) ;
    for(size_t i=0; i<fac.size() ;i++) {
        ret += fixpoLL(fac[i]) * phi(n / fac[i]) ;
        ret %= MOD ;
    }
    ret = ret * inv(n) % MOD ;
    return ret ;
}

int main()
{ 
    init_prime() ;

    LL n , k , T ;
    scanf("%I64d" , &T) ;
    while(T--) {
        scanf("%I64d%I64d%I64d" , &n ,&M , &k) ;
        for(LL i=0; i<M; i++)
            for(LL j=0; j<M; j++)
                ok.a[i][j] = 1 ;
        for(LL i=0; i<k; i++) {
            int u ,v ;
            scanf("%d%d" , &u , &v) ;
            u-- , v-- ;
            ok.a[u][v] = ok.a[v][u] = 0 ;
        }
        LL ans = solve(n) ;
        printf("%I64d\n" , ans) ;
    }

    return 0;
}


HDU 2865 Birthday Toy

与前面的题大同小异,可以按“是否与第一位相同”构造矩阵计算。

推荐播客: http://hi.baidu.com/billdu/item/62319f2554c7cac9a5275a0d

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long LL ;
const LL MOD = 1e9 + 7 ;
const int maxn = 31650 ;

struct Mat{
    LL a[2][2] ;
    Mat(int x=0){memset(a,0,sizeof(a)) ; a[0][0]=a[1][1]=x; }
    Mat operator * (const Mat &b)const {
        Mat ret;
        for(int i=0; i<2; i++)
        for(int j=0;j<2;j++){
            for(int k=0;k<2;k++)
                ret.a[i][j] += a[i][k] * b.a[k][j] ;
            ret.a[i][j] %= MOD ;
        }
        return ret ;
    }
    Mat operator ^ (LL n) {
        Mat ret(1) ;
        while(n) {
            if(n&1) ret = ret * *this ;
            *this=*this * *this ;
            n>>=1 ;
        }
        return ret ;
    }
};

int pri[maxn/4] , pri_tot ;
bool _flag[maxn+1] ;
void init_prime() {
    for(int i=2; i*i <=maxn; i++) if(!_flag[i])
        for(int j=i*i ; j<=maxn; j+=i) _flag[j] = true ;
    for(int i=2; i<=maxn; i++) if(!_flag[i]) pri[pri_tot++] = i ;
}
void gcd(LL a , LL b, LL &d , LL &x ,LL &y) {
    if(!b) d = a , x = 1 , y = 0 ;
    else gcd(b , a%b ,d , y ,x), y -= a/b * x ;
}
LL inv(LL a) {
    LL d , x , y ;
    gcd(a , MOD , d , x ,y) ;
    return d==1 ? (x+MOD)%MOD : -1 ;
}
LL Pow(LL a , LL n) {
    LL ret =  1 ;
    while(n) {
        if(n&1) ret = ret * a % MOD ;
        a = a * a % MOD ;
        n>>=1 ;
    }
    return ret ;
}
LL phi(LL n) {
    LL ret  = n ;
    for(int i=0; pri[i]*pri[i]<=n ;i++) if(n%pri[i] == 0) {
        ret = ret / pri[i] * (pri[i] - 1) ;
        while(n%pri[i] == 0) n/= pri[i] ;
    }
    if(n>1) ret = ret / n * (n-1) ;
    return ret ;
}
vector<LL>fac ;
struct Function{
    LL a[100] , p[100] , tot ;
    void FenJie(LL n) {
        tot = 0 ;
        for(int i=0; pri[i]*pri[i] <= n; i++) if(n % pri[i] == 0) {
            a[tot] = pri[i] , p[tot] = 0 ;
            while( n % pri[i] == 0) p[tot]++ , n/=pri[i] ;
            tot++ ;
        }
        if(n>1) a[tot] = n , p[tot++] = 1 ;
    }
    void dfs(LL z , int d) {
        if(d == tot) {
            fac.push_back(z) ;
            return  ;
        }
        LL ap = 1 ;
        for(int i=0; i<=p[d] ; i++) {
            dfs(z*ap , d+1) ;
            ap *= a[d] ;
        }
    }
    void get_fac(LL n) {
        fac.clear() ;
        FenJie(n) ;
        dfs(1 , 0) ;
    }
}Fun;
LL cal(LL k , LL n) {
    if(n==1) return 0;
    Mat A ;
    A.a[0][0]=k-2 , A.a[0][1]=k-1 ;
    A.a[1][0]=1   , A.a[1][1]=0 ;
    A = A ^ (n-1);
    return A.a[0][1] * k % MOD ;
}

LL solve(LL n , LL k) {
    LL sum = 0 ;
    Fun.get_fac(n) ;//  cout<<fac.size()<<endl ;
    for(size_t i=0; i<fac.size() ;i++) {
        sum += cal(k-1 , fac[i]) * phi(n/fac[i]) ;
        sum %= MOD ;
    }
    sum = sum * k % MOD ;
    sum = sum * inv(n) % MOD ;
    return sum ;
}

int main()
{ 
    ios::sync_with_stdio(0) ;
    init_prime() ;
    LL n , k;
    while(cin>>n>>k) cout<<solve(n , k)<<endl ;
    return 0;
}


HDU 2481 Toy

这题有难度,其中递推推导部分不容易。

而且n不存在逆元,解决方法是 (a/b)%c = (a%(b*c))  / b 

推荐播客: http://hi.baidu.com/spellbreaker/item/d8bb3bda5af30be6795daa93

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long LL ;
const int maxn = 31650 ;

LL MOD ;
LL mul(LL a , LL b){
    a%=MOD;
    if(b<0) a = -a , b=-b ;
    if(a<0) a += MOD;
    LL ret = 0 ;
    while(b){
        if(b&1LL && (ret+=a)>=MOD) ret -= MOD ;
        if((a<<=1) >= MOD) a -= MOD ;
        b >>= 1 ;
    }
    return ret ;
}
struct Mat{
    LL a[2][2] ;
    Mat (int x=0) {a[0][0]=a[1][1]=x ; a[0][1]=a[1][0]=0; }
    Mat operator * (const Mat & b)const {
        Mat ret ;
        for(int i=0;i<2;i++)
        for(int j=0; j<2; j++) {
            for(int k=0; k<2; k++)
                if((ret.a[i][j] += mul(a[i][k] , b.a[k][j])) >= MOD )
                 ret.a[i][j] -= MOD ;
        }
        return ret ;
    }
    Mat operator ^ (LL n) {
        Mat ret(1) ;
        while(n) {
            if(n&1) ret = ret * *this ;
            *this=*this* *this ;
            n>>=1 ;
        }
        return ret ;
    }
};

LL F(LL n) {
    if(n == 0) return 1 ;
    Mat A ;
    A.a[0][0] = 3 , A.a[0][1] = -1 ;
    A.a[1][0] = 1 , A.a[1][1] = 0 ;
    A = A^(n-1) ;
    LL ret =  mul(A.a[1][0] , 3) + A.a[1][1] ;
    while(ret >= MOD) ret -= MOD ;
    return ret ;
}
LL G(LL n) {
    if(n<=1) return 0 ;
    LL ret = F(n) - F(n-1) - 1 ;
    ret = mul(ret , 2) ;
    return ret ;
}

LL pri[maxn/4] , pri_tot ;
bool _flag[maxn+1] ;
void init_prime(){
    for(LL i=2; i*i<=maxn; i++) if(!_flag[i]) {
        for(LL j=i*i; j<=maxn; j+=i) _flag[j] = true ;
    }
    for(LL i=2; i<=maxn; i++) if(!_flag[i]) pri[pri_tot++] = i;
}

vector<LL>fac;
struct Function{ // 用于计算n的因子,保存在 vector<LL> fac
    LL a[100], p[100], tot ;
    void FenJie(LL n) {
        tot = 0 ;
        for(LL i=0; pri[i]*pri[i]<=n; i++) if(n % pri[i] == 0) {
            p[tot] = 0 ;
            a[tot] = pri[i] ;
            while(n % pri[i] ==0)  n/=pri[i] , p[tot]++ ;
            tot++ ;
        }
        if(n > 1) a[tot] = n , p[tot++] = 1  ;
    }
    void dfs(LL z , LL d) {
        if(d == tot) {
            fac.push_back(z) ;
            return ;
        }
        LL ap = 1 ;
        for(LL i=0; i<=p[d]; i++) {
            dfs(z * ap , d+1) ;
            ap *= a[d] ;
        }
    }
    void  get_fac(LL n) {
        fac.clear() ;
        FenJie(n) ;
        dfs(1 , 0) ;
    }
}sol;

LL phi(LL n) {
    LL ret = n;
    for(LL i=0; pri[i]*pri[i]<=n; i++) if(n % pri[i] == 0) {
        ret = ret / pri[i] * (pri[i] - 1) ;
        while(n % pri[i] == 0) n /= pri[i] ;
    }
    if(n > 1) ret = ret / n * (n-1) ;
    return ret ;
}

LL solve(LL n , LL m) {
    MOD = n * m ;
    LL sum = 0 ;
    sol.get_fac(n) ;
    for(size_t i = 0; i<fac.size() ;i++) {
        sum += mul(F(fac[i])+G(fac[i]) , phi(n/fac[i])) ;
        if( sum >= MOD) sum -= MOD ;
        else if(sum < 0) sum += MOD ;
    }
    sum /= n ;
    return sum ;
}

int main()
{ 
    init_prime() ;
    LL n , m;
    ios::sync_with_stdio(0) ;
    while(cin>>n>>m) {
        cout<<solve(n , m)<<endl ;
    }
    return 0;
}


H  HDU 3923 Invoker

做完前面的题,这题应该没任何难度。

import java.util.* ;
import java.math.* ;
import java.io.* ;


public class Main {
	static long MOD = (long)1e9+7 ;
	
	static public void main(String args[]) { 
		Scanner cin = new Scanner(System.in); 
		int T = cin.nextInt();
		for(int cas = 1; cas <= T; cas++) {
			long c = cin.nextLong() , n = cin.nextLong(); 
			long sum = 0 ;
			for(long i=0; i<n; i++) {
				sum += pow(c , gcd(i , n)) ;
				sum %= MOD ;
			}
			if(n%2 == 1) {
				sum += n * pow(c , (n+1)/2) ; 
				sum %= MOD ;
			}
			else {
				sum += n/2*pow(c , n/2+1) ; 
				sum += n/2*pow(c , n/2) ;
				sum %= MOD ; 
			}
			
			sum = sum * inv(2*n) % MOD ;
			int ans = (int)sum ;
			System.out.printf("Case #%d: " , cas) ;
			System.out.println(ans) ;
		}
		cin.close();
	}  
	
	static long gcd(long a,long b){
		return b==0 ? a : gcd(b , a%b) ; 
	}
	
	static long pow(long a , long n){
		long ret = 1 ;
		while(n != 0) {
			if(n%2 == 1) ret = ret * a % MOD; 
			a = a * a % MOD ;
			n>>=1 ;
		}
		return ret ;
	}
	
	static long inv(long x) {
		return pow(x , MOD-2) ;
	}
}


UVA 10601 Cubes

白书上的例题:共24种置换。

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

int cnt[10] ;
int A[15] ;
int f(int n , int m) {
    int ret = A[n/m] ;
    for(int i=1; i<=6; i++) {
        if(cnt[i] % m) return 0 ;
        ret /= A[cnt[i] / m] ;
    }
    return ret ;
}

int main()
{
    A[0]=1 ;
    for(int i=1; i<15; i++) A[i] = A[i-1] * i ;

    int T ;
    scanf("%d" , &T) ;
    while(T--) {
        memset(cnt , 0, sizeof(cnt)) ;
        for(int i=1 , x; i<=12; i++) {
            scanf("%d" ,&x);
            cnt[x]++ ;
        }
        int sum = 0 ;
        sum += f(12 , 1);
        sum += 3 * f(12 ,4) + 3 * f(12 , 2) + 3 * f(12 , 4) ;
        for(int i=1; i<=6; i++){
            for(int j=1;j<=6;j++){
                cnt[i]-- , cnt[j]-- ;
                if(cnt[i]>=0 && cnt[j]>=0)  sum += 6 * f(10 , 2) ;
                cnt[i]++ , cnt[j]++ ;
            }
        }
        sum += 4 * f(12 , 3) + 4 * f(12 , 3) ;
        int ans = sum / 24 ;
        printf("%d\n" , ans) ;
    }
    return 0;
}



UVA 11255 Necklace

同样只是练手题。

import java.util.*; 
import java.io.*; 
import java.math.*; 

public class Main {
	static BigInteger A[] = new BigInteger[50] ;
	static int  n , a[] = new int[5]; 
	
	static void init() {
		A[0] = BigInteger.ONE ;
		for(int i=1; i<=40; i++)
			A[i] = A[i-1].multiply(BigInteger.valueOf(i)) ;
	}
	static int gcd(int x, int y) {
		return y==0 ? x : gcd(y , x%y) ; 
	}
	static BigInteger rotate(int m) {
		BigInteger ret = A[n / m] ; 
		for(int i=1; i<=3 ;i++) {
			if(a[i] % m != 0) return BigInteger.ZERO ; 
			ret = ret.divide(A[a[i]/m]) ;
		}
		return ret; 
	}
	static BigInteger flip(int m)  {
		BigInteger ret = A[m / 2] ; 
		for(int i=1; i<=3; i++) {
			if(a[i] % 2 != 0 || a[i]<0 ) return BigInteger.ZERO ; 
			ret = ret.divide(A[ a[i]/2 ]) ; 
		}
		return ret ; 
	}
	//===========================================================================
	static public void main(String args[]){
		Scanner cin = new Scanner(System.in); 
		init() ; 
		
		int T = cin.nextInt(); 
		while(T-- != 0) {
			n = 0 ;
			for(int i=1; i<=3; i++){
				a[i] = cin.nextInt();
				n += a[i] ;
			} 
			BigInteger sum  = BigInteger.ZERO ; 
			// deal rotate 
			for(int i=0; i<n; i++) {
				sum = sum.add( rotate( n / gcd(i , n) ) ); 
			} 
			// deal flip
			if(n%2 == 1) {
				for(int i=1; i<=3; i++) {
					a[i] -- ;
					if(a[i] >= 0 ) sum = sum.add( BigInteger.valueOf(n).multiply( flip(n-1) ) ) ; 
					a[i] ++ ; 
				}
			}
			else {
				sum = sum.add( BigInteger.valueOf(n/2).multiply( flip(n) ) ) ; 
				for(int i=1; i<=3; i++){
					for(int j=1; j<=3; j++) {
						a[i]-- ; a[j]-- ;
						if(a[i]>=0 && a[j]>=0) 
							sum = sum.add( BigInteger.valueOf(n/2).multiply( flip(n-2) ) ) ; 
						a[i]++ ; a[j]++ ; 
					}
				}
			}
			
			// output
			BigInteger ans = sum.divide(BigInteger.valueOf(n * 2)) ; 
			System.out.println(ans.toString()) ; 
		}
		cin.close(); 
	}
}



CSU 1027 Smallbox魔方

和I题基本相同。

import java.util.*;
import java.io.* ;
import java.math.*; 

public class Main {
	static int maxn = 50 * 50 * 6 ;
	static int c[] = new int[7] ;
	static long A[] = new long[maxn+1] , invA[] = new long[maxn+1] ; 
	static long MOD = (long)1e9 + 7 ; 
	
	static public void main(String args[]) {
		Scanner cin = new Scanner(System.in) ; 
		init() ; 
		
		int T = cin.nextInt() ; 
		while(T-->0) {
			int n = cin.nextInt() ; 
			int N = 0 ; 
			for(int i=1; i<=6; i++) {
				c[i] = cin.nextInt(); 
				N += c[i] ; 
			}
			long sum = 0 ; 
			// 不动
			sum += fixpoint(N , 1) ;   //System.out.println(N); 
			// 绕对称的棱的中点的连线转180°
			sum += 6 * fixpoint(N , 2) ; 
			// 绕对顶角转 120° 、 240°
			sum += 4 * 2 * fixpoint(N , 3) ;
			// 绕相对的面的中心的连线转90° 、180°、270°
			if(n%2 == 1) {
				for(int i=1; i<=6; i++)
					for(int j=1; j<=6; j++) {
						c[i]-- ; c[j]-- ;
						if(c[i]>=0 && c[j]>=0) sum += 3 * ( 2*fixpoint(N-2 , 4) + fixpoint(N-2 , 2) ) ; 
						c[i]++;  c[j]++ ;
					}
			}
			else {
				sum += 3 * ( 2*fixpoint(N , 4) + fixpoint(N , 2) ) ; 
			}
			
			sum %= MOD ; 
			long ans = sum * inv(24) % MOD ; 
			System.out.println(ans) ; 
		}
	}
	
	static void init() {
		A[0] = 1 ; 
		for(int i=1; i<=maxn; i++) A[i] = A[i-1] * i % MOD ;
		for(int i=0; i<=maxn; i++) invA[i] = inv(A[i]) ; 
	}
	static long pow(long a , long n) {
		long ret = 1 ; 
		while(n>0) {
			if(n%2 == 1) ret = ret * a % MOD ; 
			a = a * a % MOD ;
			n>>=1 ;
		}
		return ret ;
	}
	static long inv(long a) {
		return pow(a , MOD-2) ; 
	}
	static long fixpoint(int n , int m) {
		long ret = A[n/m] ; 
		for(int i=1; i<=6; i++){
			if(c[i] % m != 0 || c[i]<0) return 0 ; 
			ret = ret * invA[c[i] / m] % MOD ; 
		}
		return ret ; 
	}
}



L  CSU 1436 Revenge of the Round Table

连续相同的个数不能超过k , dp.....

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL ;
const int maxn = 1010 ;
const LL MOD = 1000003 ;

int dp[maxn][maxn][2] ,N, K ; // dp[i][j][k] : 长为i,后面j个相同,k: 最后一位是否与第一位相同。

LL Pow(LL a , LL n) {
    LL ret =1 ;
    while(n) {
        if(n&1) ret = ret * a % MOD ;
        a = a * a % MOD ;
        n>>=1 ;
    }
    return ret ;
}
LL inv(LL a) {
    return Pow(a , MOD-2) ;
}
int phi(int n){
    int ret = n ;
    for(int i=2; i*i<=n ; i++) if(n%i==0) {
        ret = ret /i * (i-1) ;
        while(n%i == 0) n/=i ;
    }
    if(n>1) ret= ret / n *(n-1) ;
    return ret ;
}
LL fixpoint(LL n) {
    if(K>=N) return Pow(2 , n) ;
    memset(dp , 0 ,sizeof(dp)) ;
    dp[1][1][1] = 2 ;
    for(int i=1; i<n; i++) {
        for(int j=1; j<=K; j++){
            for(int k=0; k<2; k++){
                dp[i+1][j+1][k] += dp[i][j][k] ;
                dp[i+1][j+1][k] %= MOD ;

                dp[i+1][1][k^1] += dp[i][j][k] ;
                dp[i+1][1][k^1] %= MOD ;
            }
        }
    }
    LL ans = 0 ;
    for(int s=1; s<=K && s<n; s++){  // 前面已经有s个相同的了。
        for(int i=1; i<=K && i+s<=N; i++)   ans += dp[n-s][i][1] ;
        for(int i=1; i+s<=K; i++)           ans += dp[n-s][i][0] ;
        ans %= MOD ;
    }
    return ans ;
}

int main()
{
    cin>>N>>K;
    LL sum = 0 ;
    for(int i=1; i<=N; i++) if(N%i==0){
        sum += fixpoint(i) * phi(N/i) ;
    }
    sum %= MOD ;
    LL ans = sum * inv(N) % MOD ;
    cout<<ans<<endl ;
    return 0;
}


SPOJ TRANSP Transposing is Fun


SGU 282 Isomorphism


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个组合数学问题,我们可以使用 Burnside 引理来解决。Burnside 引理是一个计数定理,可以用于计算一个群在一些操作下的不动点个数。 对于这个问题,我们可以使用置换群 $S_n$,它包含 $n!$ 个置换,每个置换都对应着集合 $A$ 上的一个等价关系。我们需要计算的是在这 $n!$ 个置换中有多少个是等价的。 对于一个置换 $\sigma \in S_n$,我们定义它的循环节数为 $\operatorname{lcm}(k_1, k_2, \ldots, k_m)$,其中 $k_1, k_2, \ldots, k_m$ 是 $\sigma$ 的所有循环的长度。例如,对于置换 $(1\ 2\ 3)(4\ 5)$,它的循环节数为 $\operatorname{lcm}(3, 2) = 6$。 根据 Burnside 引理,不同等价关系的个数等于所有置换的循环节数的平均数。也就是说,我们需要计算: $$ \frac{1}{n!} \sum_{\sigma \in S_n} \operatorname{lcm}(k_1, k_2, \ldots, k_m) $$ 其中 $k_1, k_2, \ldots, k_m$ 是 $\sigma$ 的所有循环的长度。这个式子看起来很难计算,但是我们可以使用 Polya 定理来简化它。 Polya 定理是一个计数定理,可以用于计算一个群在一些操作下的循环节数。我们在这里简单介绍一下它的用法。对于一个置换群 $G$,我们可以定义它的一个操作为一个二元组 $(g, x)$,其中 $g \in G$,$x$ 是一个元素。这个操作将 $x$ 变成 $g(x)$。我们需要计算 $G$ 在所有操作下的不动点个数之和。根据 Polya 定理,这个和等于 $$ \frac{1}{|G|} \sum_{g \in G} a(g)^{c(g)} $$ 其中 $a(g)$ 是 $g$ 操作下的不动点个数,$c(g)$ 是 $g$ 的循环节数。 对于这个问题,我们可以使用 $S_n$ 的所有置换作为操作。对于一个置换 $\sigma \in S_n$,我们将它表示成一个 $n$ 元组 $(a_1, a_2, \ldots, a_n)$,其中 $a_i$ 是 $\sigma(i)$。我们定义操作 $(\tau, (a_1, a_2, \ldots, a_n))$ 将元组 $(a_1, a_2, \ldots, a_n)$ 变成 $(\tau(a_1), \tau(a_2), \ldots, \tau(a_n))$。 我们需要计算 $S_n$ 在所有操作下的不动点个数之和。根据 Polya 定理,这个和等于 $$ \frac{1}{n!} \sum_{\sigma \in S_n} 2^{c(\sigma)} $$ 其中 $c(\sigma)$ 是 $\sigma$ 的循环节数。注意到这个式子只和 $\sigma$ 的循环节数有关,不和具体的置换有关。因此,我们可以将它改写成 $$ \frac{1}{n!} \sum_{k=1}^n p(n, k) 2^{k} $$ 其中 $p(n, k)$ 表示将 $n$ 个元素划分成 $k$ 个非空的循环的方案数。它的值可以使用 Bell 数来计算。也就是说, $$ p(n, k) = \sum_{i=0}^k {k \choose i} B_{n-i} $$ 其中 $B_n$ 是第 $n$ 个 Bell 数。它的递推式是 $$ B_{n+1} = \sum_{k=0}^n {n \choose k} B_k $$ 边界条件是 $B_1 = 1$。 综上所述,我们可以使用下面的 Python 代码来解决这个问题:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值