polya定理
这就是所谓的polya定理。具体去百度文库看论文。
题意:用3种颜色给圆上n个点染色。问你有多少种染色方法。
做法:旋转有n个置换,对称也有n个置换。然后对于旋转,暴力找出循环节;对称,根据n是奇数还是偶数分开来求,循环节就根据对称来求。然后套上polya定理就好了
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <vector> 7 #include <queue> 8 9 using namespace std; 10 11 #define LL long long 12 #define eps 1e-6 13 #define inf 0x3f3f3f3f 14 #define mod 1000 15 #define MP make_pair 16 #define mnx 50 17 18 LL qpow( LL x, int k ){ 19 LL ret = 1; 20 while( k ){ 21 if( k & 1 ) ret *= x; 22 x *= x; 23 k >>= 1; 24 } 25 return ret; 26 } 27 int num[mnx], n; 28 bool vis[mnx]; 29 void dfs( int u ){ 30 vis[u] = 1; 31 int v = num[u]; 32 if( !vis[v] ) 33 dfs( v ); 34 } 35 int gao( int s ){ 36 memset( vis, 0, sizeof(vis) ); 37 for( int i = 1; i <= n; ++i ){ 38 num[i] = ( i + s - 1 ) % n + 1; 39 } 40 int ret = 0; 41 for( int i = 1; i <= n; ++i ){ 42 if( !vis[i] ) 43 dfs( i ), ret++; 44 } 45 return ret; 46 } 47 int main(){ 48 while( scanf( "%d", &n ) != EOF && n != -1 ){ 49 if( n == 0 ){ 50 puts( "0" ); continue; 51 } 52 LL sum = 0; 53 for( int i = 0; i < n; ++i ){ 54 int tmp = gao( i ); 55 sum += qpow( 3LL, tmp ); 56 } 57 if( n % 2 == 0 ){ 58 sum += qpow( 3LL, (n+2)/2 ) * ( n/2 ); 59 sum += qpow( 3LL, n/2 ) * ( n/2 ); 60 } 61 else sum += qpow( 3LL, (n+1)/2 ) * n; 62 printf( "%I64d\n", sum / 2 / n ); 63 } 64 return 0; 65 }
题意:c种颜色,手链有n个珠子,问你有多少种不同的染色方法。
做法:跟上题差不多。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <vector> 7 #include <queue> 8 9 using namespace std; 10 11 #define LL long long 12 #define eps 1e-6 13 #define inf 0x3f3f3f3f 14 #define mod 1000 15 #define MP make_pair 16 #define mnx 50 17 18 LL qpow( int x, int k ){ 19 LL ret = 1; 20 while( k ){ 21 if( k & 1 ) ret *= x; 22 x *= x; 23 k >>= 1; 24 } 25 return ret; 26 } 27 int n, num[mnx]; 28 bool vis[mnx]; 29 void dfs( int u ){ 30 vis[u] = 1; 31 int v = num[u]; 32 if( !vis[v] ) 33 dfs( v ); 34 } 35 int gao( int s ){ 36 memset( vis, 0, sizeof( vis) ); 37 for( int i = 1; i <= n; ++i ) 38 num[i] = ( i + s - 1 ) % n + 1; 39 int ret =0; 40 for( int i = 1; i <= n; ++i ){ 41 if( !vis[i] ) 42 dfs( i ), ret++; 43 } 44 return ret; 45 } 46 int main(){ 47 int c; 48 while( scanf( "%d%d", &c, &n ) != EOF && ( c || n ) ){ 49 if( n == 0 ){ 50 puts( "0" ); continue; 51 } 52 LL sum = 0; 53 for( int i = 0; i < n; ++i ){ 54 int tmp = gao( i ); 55 sum += qpow( c, tmp ); 56 } 57 if( n % 2 ) 58 sum += qpow( c, (n+1)/2 ) * n; 59 else{ 60 sum += qpow( c, (n+2)/2 ) * ( n/2 ); 61 sum += qpow( c, n/2 ) * ( n/2 ); 62 } 63 printf( "%I64d\n", sum / 2 / n ); 64 } 65 return 0; 66 }
题意:n*n的棋盘有c种颜色来染色。问你有多少种不同的染色方法。
做法:旋转4种,对称4种,找出循环节就好了。得用java做。
1 import java.math.*; 2 import java.util.*; 3 import java.io.*; 4 5 public class Main{ 6 public static void main(String args[]){ 7 Scanner cin = new Scanner( System.in ); 8 while( cin.hasNext() ){ 9 int n = cin.nextInt(); 10 BigInteger c = cin.nextBigInteger(); 11 int c1 = n*n, c3 = ( n*n + 1 ) / 2, c2 = ( n*n + 3 ) / 4, c4 = c2, c5, c6, c7, c8; 12 if( n % 2 == 0 ){ 13 c5 = c7 = n*n / 2; c6 = c8 = ( n*n - n ) / 2 + n; 14 } 15 else{ 16 c5 = c6 = c7 = c8 = ( n*n - n ) / 2 + n; 17 } 18 //System.out.println( c1 + " " + c2 + " " + c3 + " " + c4 ); 19 //System.out.println( c5 + " " + c6 + " " + c7 + " " + c8 ); 20 BigInteger eight = new BigInteger( "8" ); 21 BigInteger ans = c.pow( c1 ); 22 ans = ans.add( c.pow( c2 ) ); 23 ans = ans.add( c.pow( c3 ) ); 24 ans = ans.add( c.pow( c4 ) ); 25 ans = ans.add( c.pow( c5 ) ); 26 ans = ans.add( c.pow( c6 ) ); 27 ans = ans.add( c.pow( c7 ) ); 28 ans = ans.add( c.pow( c8 ) ); 29 System.out.println( ans.divide( eight ) ); 30 } 31 } 32 }
题意:项链有n个珠子,用n种颜色染色。只考虑旋转,不考虑对称,问有多少种不同的染色方法(答案% p )1 <= n <= 1000000000
做法:n很大,注意到gcd(n,i)就是旋转 i 个珠子的循环节。设len 为循环节的长度,则len = n / gcd( n, i );我们枚举len,则gcd( n, i ) = n/len; 我们想知道循环节为 n / len的有多少个,即gcd( n/(n/len), i/(n/len) ) = 1,即gcd( len, i/(n/len) = 1,即phi( len ),然后 ans += ( phi(len) * qpow( n, n/len ) ) % mod,因为ans最后要 / n,所以就qpow( n, n/len-1 );
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <vector> 7 #include <queue> 8 9 using namespace std; 10 11 #define LL long long 12 #define eps 1e-6 13 #define inf 0x3f3f3f3f 14 #define MP make_pair 15 #define mnx 50005 16 17 int pri[mnx], tot; 18 bool isnot[mnx]; 19 void init(){ 20 for( int i = 2; i < mnx; ++i ){ 21 if( !isnot[i] ){ 22 pri[tot++] = i; 23 } 24 for( int j = 0; j < tot && i * pri[j] < mnx; ++j ){ 25 isnot[i*pri[j]] = 1; 26 if( i % pri[j] == 0 ) 27 break; 28 } 29 } 30 } 31 int phi( int x ){ 32 int ret = x; 33 for( int i = 0; i < tot && pri[i] * pri[i] <= x; ++i ){ 34 if( x % pri[i] == 0 ) 35 ret = ret / pri[i] * ( pri[i] - 1 ); 36 while( x % pri[i] == 0 ) 37 x /= pri[i]; 38 } 39 if( x > 1 ) ret = ret / x * ( x - 1 ); 40 return ret; 41 } 42 int mod; 43 int qpow( int x, int k ){ 44 int ret = 1; 45 x %= mod; 46 while( k ){ 47 if( k & 1 ) ret = ret * x % mod; 48 x = x * x % mod; 49 k >>= 1; 50 } 51 return ret; 52 } 53 int n; 54 void solve(){ 55 int sum = 0; 56 int i; 57 for( i = 1; i * i < n; ++i ){ 58 if( n % i == 0 ){ 59 int tmp = n / i; 60 sum = ( sum + phi(i) % mod * qpow( n, tmp-1 ) % mod ) % mod; 61 sum = ( sum + phi(tmp) % mod * qpow( n, i-1 ) % mod ) % mod; 62 } 63 } 64 if( i * i == n ) 65 sum = ( sum + phi(i) * qpow( n, i-1 ) % mod ) % mod; 66 printf( "%d\n", sum ); 67 } 68 int main(){ 69 int cas; 70 init(); 71 scanf( "%d", &cas ); 72 while( cas-- ){ 73 scanf( "%d%d", &n, &mod ); 74 solve(); 75 } 76 return 0; 77 }
题意:有a,b,c三种颜色的珠子,问你用这三种颜色的珠子,组成n = a + b + c的项链有多少种方法。
做法:旋转有n种,循环节是gcd( n, i )。对称的时候分奇数和偶数的情况。奇数情况,关于某个珠子对称,这个珠子可能是a或者b或者c的颜色,循环节长度为2,有n种情况。偶数的时候,关于两个珠子对称的时候,如果对称的珠子是不同颜色,那么有n种情况;对称的珠子同种颜色,有n/2种情况;不是关于珠子对称,而是关于对边的中点对称,有n/2种情况。因为颜色有限制,所以得用组合数计算。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <vector> 7 #include <queue> 8 9 using namespace std; 10 11 #define LL long long 12 #define eps 1e-6 13 #define inf 0x3f3f3f3f 14 #define MP make_pair 15 #define mnx 42 16 17 LL C[mnx][mnx]; 18 void init(){ 19 for( int i = 0; i < mnx; ++i ){ 20 C[i][0] = C[i][i] = 1; 21 for( int j = 1; j < i; ++j ) 22 C[i][j] = C[i-1][j] + C[i-1][j-1]; 23 } 24 } 25 int gcd( int a, int b ){ 26 return b == 0 ? a : gcd( b, a % b ); 27 } 28 LL calc( int a, int b, int c, int len, int m ){ 29 if( a < 0 || b < 0 || c < 0 ) return 0; 30 if( a % len || b % len || c % len ) return 0; 31 a /= len, b /= len; 32 return C[m][a] * C[m-a][b]; 33 } 34 int main(){ 35 int cas; 36 init(); 37 scanf( "%d", &cas ); 38 while( cas-- ){ 39 int a, b, c, n; 40 scanf( "%d%d%d", &a, &b, &c ); 41 n = a + b + c; 42 LL sum = 0; 43 for( int i = 0; i < n; ++i ){ 44 int tmp = gcd( n, i ); 45 sum += calc( a, b, c, n/tmp, tmp ); 46 } 47 if( n % 2 ){ 48 sum += calc( a-1, b, c, 2, n/2 ) * n; 49 sum += calc( a, b-1, c, 2, n/2 ) * n; 50 sum += calc( a, b, c-1, 2, n/2 ) * n; 51 } 52 else{ 53 sum += calc( a, b, c, 2, n/2 ) * n / 2; 54 sum += calc( a-1, b-1, c, 2, (n-2)/2 ) * n; 55 sum += calc( a-1, b, c-1, 2, (n-2)/2 ) * n; 56 sum += calc( a, b-1, c-1, 2, (n-2)/2 ) * n; 57 sum += calc( a-2, b, c, 2, (n-2)/2 ) * n / 2; 58 sum += calc( a, b-2, c, 2, (n-2)/2 ) * n / 2; 59 sum += calc( a, b, c-2, 2, (n-2)/2 ) * n / 2; 60 } 61 printf( "%lld\n", sum / 2 / n ); 62 } 63 return 0; 64 }
题意:有12条边,每条边的颜色不定,问你有多少种方法组成一个正方体。
做法:总共有24个置换。不动时候1种,循环节是12,循环节长度是1。绕对面的中心的连线旋转90度,180度,270度,有3组连线,总共9种情况:绕90度,270度的时候,循环节是3,循环节长度是4。绕对顶点的连线旋转120度,240度,有4组对顶点,总共8种情况,旋转的时候循环节都是4,循环节长度是3。绕对棱旋转180度,有6组对棱,共6种情况,注意到有一组对棱在旋转的时候是不变的,因此循环节是7,然后枚举这组对棱涂的颜色,剩下的再进行calc。最后ans/24
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <vector> 7 #include <queue> 8 9 using namespace std; 10 11 #define LL long long 12 #define eps 1e-6 13 #define inf 0x3f3f3f3f 14 #define mod 1000 15 #define MP make_pair 16 #define mnx 15 17 18 LL C[mnx][mnx]; 19 void init(){ 20 for( int i = 0; i < mnx; ++i ){ 21 C[i][0] = C[i][i] = 1; 22 for( int j = 1; j < i; ++j ) 23 C[i][j] = C[i-1][j] + C[i-1][j-1]; 24 } 25 } 26 int a[mnx], b[mnx]; 27 LL calc( int len, int m ){ 28 for( int i = 1; i <= 6; ++i ){ 29 if( b[i] % len || b[i] < 0 ) 30 return 0; 31 b[i] /= len; 32 } 33 LL ret = 1; 34 for( int i = 1; i <= 6; ++i ){ 35 ret *= C[m][b[i]]; 36 m -= b[i]; 37 } 38 return ret; 39 } 40 void solve(){ 41 LL sum = 0; 42 memcpy( b, a, sizeof(a) ); 43 sum += calc( 1, 12 ); 44 memcpy( b, a, sizeof(a) ); 45 sum += calc( 4, 3 ) * 6; 46 memcpy( b, a, sizeof(a) ); 47 sum += calc( 2, 6 ) * 3; 48 memcpy( b, a, sizeof(a) ); 49 sum += calc( 3, 4 ) * 8; 50 for( int i = 1; i <= 6; ++i ){ 51 for( int j = i; j <= 6; ++j ){ 52 memcpy( b, a, sizeof(a) ); 53 b[i]--, b[j]--; 54 if( i != j ) 55 sum += calc( 2, 5 ) * 12; 56 else 57 sum += calc( 2, 5 ) * 6; 58 } 59 } 60 printf( "%lld\n", sum / 24 ); 61 } 62 int main(){ 63 int cas; 64 init(); 65 scanf( "%d", &cas ); 66 while( cas-- ){ 67 memset( a, 0, sizeof(a) ); 68 for( int i = 0; i < 12; ++i ){ 69 int c; 70 scanf( "%d", &c ); 71 a[c]++; 72 } 73 solve(); 74 } 75 }
题意:有n个珠子组成的手链,有m种颜色的珠子( m <= 10 ),有k个限定的条件(颜色a不能和颜色b相连)。问你有多少种方法组成不同的手链。
做法:初始化矩阵g全部为1,对于限定条件,g[a][b] = 0。然后对于循环节为 tmp 时,计算颜色矩阵经过 tmp 次循环后sigma g[i][i]就是在该置换下着色方案的不动置换的个数。因为有mod,所以最后的时候不能/n,而是取逆元。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <vector> 7 #include <queue> 8 9 using namespace std; 10 11 #define LL long long 12 #define eps 1e-6 13 #define inf 0x3f3f3f3f 14 #define MP make_pair 15 #define mnx 50005 16 #define mod 9973 17 18 int pri[mnx], tot; 19 bool isnot[mnx]; 20 void init(){ 21 for( int i = 2; i < mnx; ++i ){ 22 if( !isnot[i] ){ 23 pri[tot++] = i; 24 } 25 for( int j = 0; j < tot && i * pri[j] < mnx; ++j ){ 26 isnot[i*pri[j]] = 1; 27 if( i % pri[j] == 0 ) 28 break; 29 } 30 } 31 } 32 int phi( int x ){ 33 int ret = x; 34 for( int i = 0; i < tot && pri[i] * pri[i] <= x; ++i ){ 35 if( x % pri[i] == 0 ) 36 ret = ret / pri[i] * ( pri[i] - 1 ); 37 while( x % pri[i] == 0 ) 38 x /= pri[i]; 39 } 40 if( x > 1 ) ret = ret / x * ( x - 1 ); 41 return ret % mod; 42 } 43 int n, m; 44 struct Matrix{ 45 int g[12][12]; 46 Matrix operator * ( const Matrix &b ) const { 47 Matrix ret; 48 memset( ret.g, 0, sizeof(ret.g) ); 49 for( int i = 1; i <= m; ++i ) 50 for( int j = 1; j <= m; ++j ){ 51 for( int k = 1; k <= m; ++k ) 52 ret.g[i][j] += g[i][k] * b.g[k][j]; //要是ret.g[i][j] = ( g[i][k] * b.g[k][j] + ret.g[i][j] ) % mod 53 ret.g[i][j] %= mod; //这样会慢很多,c++会tle,g++才能过。因为%mod比较耗时间 54 } 55 return ret; 56 } 57 }; 58 Matrix qpow( Matrix x, int k ){ 59 Matrix ret; 60 memset( ret.g, 0, sizeof(ret.g) ); 61 for( int i = 1; i <= m; ++i ) 62 ret.g[i][i] = 1; 63 while( k ){ 64 if( k & 1 ) ret = ret * x; 65 x = x * x; 66 k >>= 1; 67 } 68 return ret; 69 } 70 int qpow( int x, int k ){ 71 int ret = 1; 72 x %= mod; 73 while( k ){ 74 if( k & 1 ) ret = ret * x % mod; 75 x = x * x % mod; 76 k >>= 1; 77 } 78 return ret; 79 } 80 int k; 81 Matrix a; 82 int gao( int x ){ 83 int res = 0; 84 Matrix ret = qpow( a, x ); 85 for( int i = 1; i <= m; ++i ) 86 res += ret.g[i][i]; 87 return res % mod; 88 } 89 void solve(){ 90 int ans = 0; 91 int i; 92 for( i = 1; i * i < n; ++i ){ 93 if( n % i == 0 ){ 94 int tmp = n / i; 95 ans = ( ans + phi(tmp) * gao(i) ) % mod; 96 ans = ( ans + phi(i) * gao(tmp) ) % mod; 97 } 98 } 99 if( i * i == n ) 100 ans = ( ans + phi(i) * gao(i) ) % mod; 101 int inv = qpow( n % mod, mod - 2 ) % mod; 102 ans = ( ans * inv ) % mod; 103 printf( "%d\n", ans ); 104 } 105 int main(){ 106 freopen( "tt.txt", "r", stdin ); 107 int cas; 108 init(); 109 scanf( "%d", &cas ); 110 while( cas-- ){ 111 scanf( "%d%d%d", &n, &m, &k ); 112 for( int i = 1; i <= m; ++i ) 113 for( int j = 1; j <= m; ++j ) 114 a.g[i][j] = 1; 115 for( int i = 0; i < k; ++i ){ 116 int u, v; 117 scanf( "%d%d", &u, &v ); 118 a.g[u][v] = a.g[v][u] = 0; 119 } 120 solve(); 121 } 122 return 0; 123 }