BZOJ 4816 数字表格
数字表格
【问题描述】
Doris刚刚学习了fibonacci数列。用f[i]表示数列的第i项,那么
f[0]=0
f[1]=1
f[n]=f[n-1]+f[n-2],n>=2
Doris用老师的超级计算机生成了一个n×m的表格,第i行第j列的格子中的数是f[gcd(i,j)],其中gcd(i,j)表示i,
j的最大公约数。Doris的表格中共有n×m个数,她想知道这些数的乘积是多少。答案对10^9+7取模。
【输入格式】
有多组测试数据。
第一个一个数T,表示数据组数。
接下来T行,每行两个数n,m
T<=1000,1<=n,m<=10^6
【输出格式】
输出T行,第i行的数是第i组数据的结果
【样例输入】
3
2 3
4 5
6 7
【样例输出】
1
6
960
题解:
可以用O(nlog2n)的普通筛法预处理出来的
那么剩下的部分就是逆元和分块了
1 #include<cmath> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 const int maxt = 1e3 + 233; 9 const int maxn = 1e6 + 233; 10 const int mod = 1e9 + 7; 11 int T; 12 int n[maxt], m[maxt]; 13 int f[maxn], g[maxn], inv[maxn]; 14 bool vis[maxn]; 15 int p[maxn], mu[maxn]; 16 int suma[maxn], sumb[maxn]; 17 inline void Scan(int &x) 18 { 19 char c; 20 bool o = false; 21 while(!isdigit(c = getchar())) o = (c != '-') ? o : true; 22 x = c - '0'; 23 while(isdigit(c = getchar())) x = x * 10 + c - '0'; 24 if(o) x = -x; 25 } 26 inline void Fib(int n) 27 { 28 f[0] = 0, f[1] = 1; 29 for(int i = 2; i <= n; ++i) 30 f[i] = (f[i - 1] + f[i - 2]) % mod; 31 } 32 inline void Mobius(int n) 33 { 34 mu[1] = 1; 35 int num = 0; 36 for(int i = 2; i <= n; ++i) 37 { 38 if(!vis[i]) 39 { 40 p[++num] = i; 41 mu[i] = -1; 42 } 43 for(int j = 1; j <= num; ++j) 44 { 45 int s = i * p[j]; 46 if(i * p[j] > n) break; 47 vis[s] = true; 48 if(!(i % p[j])) 49 { 50 mu[s] = 0; 51 break; 52 } 53 else mu[s] = -mu[i]; 54 } 55 } 56 } 57 inline int Pow(int x, int n) 58 { 59 int sum = 1; 60 while(n) 61 { 62 if(n & 1) sum = (long long) sum * x % mod; 63 x = (long long) x * x % mod; 64 n >>= 1; 65 } 66 return sum; 67 } 68 inline void Inv(int n) 69 { 70 for(int i = 1; i <= n; ++i) 71 inv[i] = Pow(f[i], mod - 2); 72 } 73 inline void Part(int n) 74 { 75 for(int i = 1; i <= n; ++i) g[i] = 1; 76 for(int i = 2; i <= n; ++i) 77 for(int j = i; j <= n; j += i) 78 { 79 if(mu[j / i] == -1) g[j] = (long long) g[j] * inv[i] % mod; 80 if(mu[j / i] == 1) g[j] = (long long) g[j] * f[i] % mod; 81 } 82 } 83 inline void Sum(int n) 84 { 85 suma[0] = 1; 86 for(int i = 1; i <= n; ++i) suma[i] = (long long) suma[i - 1] * g[i] % mod; 87 sumb[n] = Pow(suma[n], mod - 2); 88 for(int i = n - 1; i >= 1; --i) sumb[i] = (long long) sumb[i + 1] * g[i + 1] % mod; 89 sumb[0] = 1; 90 } 91 inline int Ans(int n, int m) 92 { 93 int last; 94 int ans = 1; 95 int c = min(n, m); 96 for(int i = 1; i <= c; i = last + 1) 97 { 98 last = min(n / (n / i), m / (m / i)); 99 ans = (long long) ans * Pow((long long) suma[last] * sumb[i - 1] % mod, (long long) (n / i) * (m / i) % (mod - 1)) % mod; 100 } 101 return ans; 102 } 103 int main() 104 { 105 Scan(T); 106 int maxn = 0; 107 for(int i = 1; i <= T; ++i) 108 { 109 Scan(n[i]), Scan(m[i]); 110 maxn = max(maxn, max(n[i], m[i])); 111 } 112 Fib(maxn); 113 Mobius(maxn); 114 Inv(maxn); 115 Part(maxn); 116 Sum(maxn); 117 for(int i = 1; i <= T; ++i) printf("%d\n", Ans(n[i], m[i])); 118 }