題意
給定,求
表示取遍所有長度爲的序列,且爲到之間的整數。
分析
先考慮這個問題的弱化版:求
第一種算法是
可以預處理
的前綴積,然後通過分塊可以求出答案。
第二種算法是通過min-max反演得到
如果令
那麼
注意到只有種取值,只要求出這些,就能夠通過分塊求出答案。問題在於如何求。令
且
由注意到,因此
用上式計算,複雜度和杜教篩類似,都是。
回到原問題,類似可設
那麼
如果求出了和以及的前綴積,就可以分塊來求了。求的前綴積可以通過
因此只需要預處理階乘的前綴積。
用求相同的方式求求和。令
那麼
令
那麼
得到
在求的時候,可以當較小時用第一種算法,較大時用第二種算法。
代碼
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 1000000007;
const int MOD1 = MOD - 1;
const int N = 1e8;
const int M = 5e6;
const int K = 2e4 + 5;
int n, m, jc1[N + 5], jc2[N + 5], po[M + 5], tot, prime[M / 10], inv[M + 5], pro[M + 5], pro_inv[M + 5];
int w[K], w1[K], g[K], G[K], G1[K], sqrtm, h1[K], h2[K];
bool not_prime[M + 5];
int ksm(int x, int y, int mo)
{
int ans = 1;
while (y)
{
if (y & 1) ans = (LL)ans * x % mo;
x = (LL)x * x % mo; y >>= 1;
}
return ans;
}
void pre()
{
po[1] = 1;
for (int i = 2; i <= M; i++)
{
if (!not_prime[i]) po[i] = ksm(i, n, MOD1);
for (int j = 1; j <= tot && i * prime[j] <= M; j++)
{
po[i * prime[j]] = (LL)po[i] * po[prime[j]] % MOD1;
if (i % prime[j] == 0) break;
}
}
}
void init()
{
jc1[0] = jc2[0] = inv[0] = inv[1] = 1;
for (int i = 1; i <= N; i++) jc1[i] = (LL)jc1[i - 1] * i % MOD, jc2[i] = (LL)jc2[i - 1] * jc1[i] % MOD;;
for (int i = 2; i <= M; i++) inv[i] = (LL)(MOD - MOD / i) * inv[MOD % i] % MOD;
for (int i = 0; i <= M; i++) pro[i] = pro_inv[i] = 1;
for (int i = 2; i <= M; i++)
{
if (!not_prime[i])
{
prime[++tot] = i;
for (int j = 1; j <= M / i; j *= i) pro[i * j] = i, pro_inv[i * j] = inv[i];
}
pro[i] = (LL)pro[i - 1] * pro[i] % MOD;
pro_inv[i] = (LL)pro_inv[i - 1] * pro_inv[i] % MOD;
for (int j = 1; j <= tot && i * prime[j] <= M; j++)
{
not_prime[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
void pre_calc_g(int m)
{
int num = 0;
for (int i = 1; i <= m; i = m / (m / i) + 1) w1[++num] = m / i;
int t = ksm(m, n, MOD1);
for (int i = num; i >= 1; i--)
{
int p = w1[i], id = p <= sqrtm ? h1[p] : h2[m / p];
g[id] = (t + MOD1 - ksm(m - p, n, MOD1)) % MOD1;
for (int j = 2, last; j <= p; j = last + 1)
{
last = p / (p / j);
int q = p / j, id1 = q <= sqrtm ? h1[q] : h2[m / q];
(g[id] += MOD1 - (LL)(last - j + 1) * g[id1] % MOD1) %= MOD1;
}
}
}
int get_S(int m)
{
if (m <= M)
{
int ans = ksm(pro[m], po[m], MOD);
for (int i = 1, last; i <= m; i = last + 1)
{
last = m / (m / i);
ans = (LL)ans * ksm((LL)pro_inv[last] * pro[i - 1] % MOD, po[m - m / i], MOD) % MOD;
}
return ans;
}
pre_calc_g(m);
int ans = 1;
for (int i = 1, last; i <= m; i = last + 1)
{
last = m / (m / i);
int p = m / i, id = p <= sqrtm ? h1[p] : h2[m / p];
ans = (LL)ans * ksm((LL)jc1[last] * ksm(jc1[i - 1], MOD - 2, MOD) % MOD, g[id], MOD) % MOD;
}
return ans;
}
void pre_calc_G(int num)
{
for (int i = num; i >= 1; i--)
{
int p = w[i], id = p <= sqrtm ? h1[p] : h2[m / p];
G1[id] = ksm(p, n, MOD1); G[id] = 1;
for (int j = 2, last; j <= p; j = last + 1)
{
last = p / (p / j);
int q = p / j, id1 = q <= sqrtm ? h1[q] : h2[m / q];
(G1[id] += MOD1 - (LL)(last - j + 1) * G1[id1] % MOD1) %= MOD1;
G[id] = (LL)G[id] * ksm(G[id1], last - j + 1, MOD) % MOD;
G[id] = (LL)G[id] * ksm((LL)jc1[last] * ksm(jc1[j - 1], MOD - 2, MOD) % MOD, G1[id1], MOD) % MOD;
}
G[id] = (LL)get_S(p) * ksm(G[id], MOD - 2, MOD) % MOD;
}
}
int get_pro(int l, int r)
{
int ans = (LL)ksm(jc1[r], r, MOD) * ksm(jc2[r - 1], MOD - 2, MOD) % MOD;
if (l <= 1) return ans;
int w = (LL)ksm(jc1[l - 1], l - 1, MOD) * ksm(jc2[l - 2], MOD - 2, MOD) % MOD;
return (LL)ans * ksm(w, MOD - 2, MOD) % MOD;
}
int solve()
{
pre();
int num = 0; sqrtm = sqrt(m);
for (int i = 1, last; i <= m; i = last + 1)
{
last = m / (m / i); w[++num] = m / i;
if (m / i <= sqrtm) h1[m / i] = num;
else h2[last] = num;
}
pre_calc_G(num);
int ans = 1;
for (int i = 1, last; i <= m; i = last + 1)
{
last = m / (m / i);
int p = m / i, id = p <= sqrtm ? h1[p] : h2[m / p];
ans = (LL)ans * ksm(G[id], (LL)(last - i + 1) * (last + i) / 2 % MOD1, MOD) % MOD;
ans = (LL)ans * ksm(get_pro(i, last), G1[id], MOD) % MOD;
}
return ans;
}
int main()
{
int T; scanf("%d", &T);
init();
while (T--)
{
scanf("%d%d", &n, &m);
printf("%d\n", solve());
}
return 0;
}