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 }
posted @ 2017-05-23 17:49  草根柴鸡  阅读(298)  评论(0编辑  收藏  举报