HDU_2865
由于有相同颜色不能相邻这一限制,我们就需要用dp去计算burnside引理中的C(f)了。
按常规套路来,先将N的约数找到,接着对于每个任意约数R,就可以找到循环节数量为R的置换数了,然后需要用dp计算出对于有R个循环节的置换下的“不动方案”的数量。
首先,中间的圆圈的颜色是任意的,而且对于每种颜色而言“不动方案”的数量是一致的,也就是说中间的圆圈有K个颜色可选,而我们只需要计算任意一个情况就可以了,最后乘K就是总数。
接下来小圆圈就剩下K-1个颜色可选了,而我们需要计算长度为R的一串珠子的合法方案数,所谓合法就是相邻的珠子不能同色,以及首尾珠子不能同色。这时第1个珠子有K-1个颜色可选,而且对于其选择任意一种颜色,最终的结果都是一样的,所以我们只需计算其中一种情况,最后乘以K-1就可以了。
比如我第1个珠子选了黄色,那么在dp的时候就可以用f[i][1]表示第i个珠子是黄色的合法方案数,f[i][0]表示第i个珠子不是黄色的合法方案数,那么不难得到f[i][1]=f[i-1][0],f[i][0]=(K-3)*f[i-1][0]+(K-2)*f[i-1][1],最后的合法方案数就是f[R][0],由于N比较大,我们可以用二分矩阵的方法加速求解过程。
到此,C(f)已经计算出来了,接下来的工作就是求N的逆元并计算最终的结果了。
另外推荐一个讲解得很详细的用dp计算Burnside中C(f)的一篇文章:http://hi.baidu.com/billdu/item/62319f2554c7cac9a5275a0d。
#include<stdio.h> #include<string.h> #include<algorithm> #define MAXD 40010 #define MOD 1000000007 using namespace std; int N, K, isprime[MAXD], prime[MAXD], P, d[MAXD], D; struct Matrix { long long a[2][2]; void init() { memset(a, 0, sizeof(a)); } }unit, mat; Matrix multiply(Matrix &x, Matrix &y) { int i, j, k; Matrix z; z.init(); for(i = 0; i < 2; i ++) for(j = 0; j < 2; j ++) { for(k = 0; k < 2; k ++) z.a[i][j] += x.a[i][k] * y.a[k][j]; z.a[i][j] %= MOD; } return z; } void prepare() { int i, j, k = 40000; memset(isprime, -1, sizeof(isprime)); P = 0; for(i = 2; i <= k; i ++) if(isprime[i]) { prime[P ++] = i; for(j = i * i; j <= k; j += i) isprime[j] = 0; } } void divide(int n) { int i; D = 0; for(i = 1; i * i <= n; i ++) if(n % i == 0) { d[D ++] = i; if(n / i != i) d[D ++] = n / i; } sort(d, d + D); } int euler(int n) { int i, x, ans; ans = x = n; for(i = 0; i < P && prime[i] * prime[i] <= n; i ++) if(x % prime[i] == 0) { ans = ans / prime[i] * (prime[i] - 1); while(x % prime[i] == 0) x /= prime[i]; } if(x > 1) ans = ans / x * (x - 1); return ans; } void powmod(Matrix &unit, Matrix &mat, int n) { while(n) { if(n & 1) unit = multiply(mat, unit); n >>= 1; mat = multiply(mat, mat); } } void exgcd(long long a, long long b, long long &x, long long &y) { if(b == 0) x = 1, y = 0; else exgcd(b, a % b, y, x), y -= x * (a / b); } void solve() { int i, j; long long x, y, ans = 0, n; divide(N); for(i = 0; i < D; i ++) { n = euler(N / d[i]); unit.init(); unit.a[1][0] = 1; mat.a[0][0] = K - 3, mat.a[0][1] = K - 2, mat.a[1][0] = 1, mat.a[1][1] = 0; powmod(unit, mat, d[i] - 1); ans = (ans + ((n * K % MOD) * (K - 1) % MOD) * unit.a[0][0] % MOD) % MOD; } exgcd(N, MOD, x, y); x = (x % MOD + MOD) % MOD; ans = ans * x % MOD; printf("%lld\n", ans); } int main() { prepare(); while(scanf("%d%d", &N, &K) == 2) { solve(); } return 0; }