HDU_2481
由于需要处理循环同构的问题,明显需要用burnside引理,于是关键问题就在于对于一个N个约数R(表示一共有R个循环节),连续R个珠子一共可以形成多少种合法的方案。
这个让我联想到了之前做过的“FJOI 2007 轮状病毒”这个题目,不难发现连续R个珠子所能形成的合法方案,和R个珠子形成的轮状病毒的数目是一致的,因此我们完全可以把那个题目的递推式拿过来做dp的递推式,我推的递推公式在轮状病毒这个题目的解题报告里面:http://www.cnblogs.com/staginner/archive/2012/03/27/2420347.html,用这个递推式去dp的话需要构造4*4的矩阵,然后用二分矩阵的方法求解,网上还可以搜到其他的递推过程。
这样dp的环节就可以搞定了,其他的按burnside引理的套路来就可以了。最后由于模的那个数M不一定和N互质,所以不能像MOD是素数那样去求N的逆元再相乘了。借鉴了一下网上看到的方法,令MOD=M*N,算出结果之后直接除以N就可以了。
此外,即便算法想到了也一定不能忽视了代码上的常数优化,我一开始交上去便TLE了,后来翻翻别人的解题报告发现除了dp的部分不太一样就剩下常数优化了,做了几处优化之后便TLE->2500ms->500ms,效率有了质的飞跃……所以以后还是要注重代码的常数优化呀。
常数优化的地方已经在代码里加以说明了。
#include<stdio.h> #include<string.h> #define MAXD 40010 int N, isprime[MAXD], prime[MAXD], P, p[MAXD], pn; long long M; struct Matrix { long long a[4][4]; void init() { memset(a, 0, sizeof(a)); } }unit, mat[40]; long long mul(long long x, long long y) { long long ans = 0; x %= M; while(y) { /* if(y & 1) ans = (ans + x) % M; y >>= 1; x = (x + x) % M; 用条件判断和减法取代模运算,常数优化 */ if((y & 1) && (ans += x) >= M) ans -= M; y >>= 1; if((x <<= 1) >= M) x -= M; } return ans; } Matrix multiply(Matrix &x, Matrix &y) { int i, j, k; Matrix z; z.init(); for(i = 0; i < 4; i ++) for(k = 0; k < 4; k ++) if(x.a[i][k]) { for(j = 0; j < 4; j ++) if(y.a[k][j]) z.a[i][j] = (z.a[i][j] + mul(x.a[i][k], y.a[k][j])) % M; } 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; } } int euler(int n) { int i, ans = n; for(i = 0; i < pn; i ++) if(n % p[i] == 0) ans = ans / p[i] * (p[i] - 1); return ans; } void divide(int n) { int i, j; pn = 0; for(i = 0; i < P && prime[i] * prime[i] <= n; i ++) if(n % prime[i] == 0) { p[pn ++] = prime[i]; while(n % prime[i] == 0) n /= prime[i]; } if(n > 1) p[pn ++] = n; } void initmat() //预先处理出递推关系矩阵的2^x幂,常数优化 { int i; mat[0].init(); mat[0].a[0][0] = 2, mat[0].a[0][2] = 1, mat[0].a[0][3] = M - 1; mat[0].a[1][0] = mat[0].a[1][1] = mat[0].a[1][2] = 1; mat[0].a[2][0] = 1, mat[0].a[2][2] = 2, mat[0].a[2][3] = M - 1; mat[0].a[3][2] = 1; for(i = 1; i < 32; i ++) mat[i] = multiply(mat[i - 1], mat[i - 1]); } void powmod(Matrix &unit, int n) { int i; for(i = 0; n; i ++, n >>= 1) if(n & 1) unit = multiply(mat[i], unit); } void dfs(int cur, int R, int x, long long &ans) { int i, cnt = 0, t = 1; if(cur == pn) { int n = euler(N / R); if(R == 1) ans = (ans + n) % M; else { unit.init(); unit.a[0][0] = 3, unit.a[1][0] = 2, unit.a[2][0] = 3, unit.a[3][0] = 1; powmod(unit, R - 2); ans = (ans + mul(n, unit.a[0][0] + unit.a[1][0])) % M; } return ; } while(x % p[cur] == 0) ++ cnt, x /= p[cur]; for(i = 0; i <= cnt; i ++) { dfs(cur + 1, R * t, x, ans); t *= p[cur]; } } void solve() { int i; long long ans, x, y, n; ans = 0; divide(N); initmat(); dfs(0, 1, N, ans); printf("%I64d\n", ans / N); } int main() { prepare(); while(scanf("%d%I64d", &N, &M) == 2) { M *= N; solve(); } return 0; }