HDU_3441
首先声明一点,对于A=1的情况最好特判,因为有些处理措施对于A=1的情况会造成死循环从而TLE。
思路其实还算比较直接,但是处理起来比较繁琐。由于小正方形旋转同构,那么对于一个特定的B我们至少要先求得有多少本质不同的小正方形,这一点可以用polya定理计算出来。
接着,对于一组特定的B、K, 拼成的这个图形依然循环同构,但我们之前已经算出了有多少本质不同的小正方形,于是可以将这些本质不同的小正方形看成不同的颜色,于是再用一次polya定理就可以计算出结果。而中间那个单位正方形的颜色是无所谓的,所以结果要乘以C,这样就得到了一组B、K对应的结果。
那么剩下的问题就是如可去确定有多少组B、K了。将原表达式变形之后可以得到K=(A-1)*(A+1)/(B*B),这时就会发现无论是B还是K,都是由A-1和A+1的素因子组合而成的,因此我们可以将这些素因子再单独放到一个表中,然后就可以枚举出每一组B、K了。同时,在计算K的某个约数的欧拉函数时我们还要用到这个新的素数表。
#include<stdio.h> #include<string.h> #include<algorithm> #define MOD 1000000007 #define MAXD 40010 using namespace std; int isprime[MAXD], prime[MAXD], P, p[MAXD], pn; long long A, C, K, ANS, T; 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); } long long powmod(long long a, long long n) { long long ans = 1; while(n) { if(n & 1) ans = ans * a % MOD; n >>= 1; a = a * a % MOD; } return ans; } 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; } } long long block(long long n, long long c) { long long ans, x, y; ans = powmod(c, n * n); ans = (ans + 2 * powmod(c, n * n / 4 + (n & 1))) % MOD; ans = (ans + powmod(c, n * n / 2 + (n & 1))) % MOD; exgcd(4, MOD, x, y); x = (x % MOD + MOD) % MOD; ans = ans * x % MOD; return ans; } long long euler(long long n) { int i; long long ans = n; for(i = 0; i < pn; i ++) if(n % p[i] == 0) ans = ans / p[i] * (p[i] - 1); return ans; } long long prepareBK() { int i, j, nx, ny, x, y, cnt; long long N = 1; nx = x = A - 1, ny = y = A + 1; pn = 0; for(i = 0; i < P && prime[i] * prime[i] <= ny; i ++) { cnt = 0; if(x % prime[i] == 0 || y % prime[i] == 0) p[pn ++] = prime[i]; while(x % prime[i] == 0) ++ cnt, x /= prime[i]; while(y % prime[i] == 0) ++ cnt, y /= prime[i]; for(j = 0, cnt /= 2; j < cnt; j ++) N *= prime[i]; } if(x > y) i = x, x = y, y = i; if(x > 1) p[pn ++] = x; if(y > 1) p[pn ++] = y; if(x == y) N *= x; return N; } void dfs(int cur, long long R, long long x, long long &c) { int i, cnt = 0; long long t = 1; if(cur == pn) { long long ans, n, x, y; n = euler(K / R) % MOD; T = (T + n * powmod(c, R)) % MOD; return ; } while(x % p[cur] == 0) x /= p[cur], ++ cnt; for(i = 0; i <= cnt; i ++) { dfs(cur + 1, R * t, x, c); t *= p[cur]; } } void findB(int cur, long long B, long long x) { int i, cnt = 0; long long t = 1; if(cur == pn) { long long n, x, y, c; c = block(B, C); K = (A * A - 1) / (B * B); T = 0; dfs(0, 1, K, c); exgcd(K, MOD, x, y); x = (x % MOD + MOD) % MOD; T = (T * x) % MOD; ANS = (ANS + T * C) % MOD; return ; } while(x % p[cur] == 0) ++ cnt, x /= p[cur]; for(i = 0; i <= cnt; i ++) { findB(cur + 1, B * t, x); t *= p[cur]; } } void solve() { ANS = 0; findB(0, 1, prepareBK()); printf("%I64d\n", ANS); } int main() { int t, tt; prepare(); scanf("%d", &t); for(tt = 0; tt < t; tt ++) { printf("Case %d: ", tt + 1); scanf("%I64d%I64d", &A, &C); if(A == 1) printf("%I64d\n", C); else solve(); } return 0; }