【校内训练2019-04-09】Wsm

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/89146962

【思路要点】

  • 考虑合法序列 A A 的性质:
    ( 1 ) (1) A A 包含 0 0
    ( 2 ) (2) 、若 0 x 1 0\sim x-1 均在 A A 中,那么对于任意自然数 k k k x ( k + 1 ) x 1 kx\sim (k+1)x-1 或是均在 A A 中,或是均不在 A A 中。
    ( 3 ) (3) 、若 A A 中次小的元素为 x x ,不是 x x 的倍数的元素一定不在 A A 中。
  • 以上三点性质均不难证明,并且,对于任意满足以上性质的序列 A A ,我们都可以构造出合适的序列 B B
  • 考虑如何生成所有合法的序列 A A
  • f ( i , j ) f(i,j) 表示当 S = i × j , m = i S=i\times j,m=i 时,包含 1 1 的序列 A A 的个数, g ( i , j ) g(i,j) 表示当 S = i × j , m = i S=i\times j,m=i 时,不包含 1 1 的序列 A A 的个数,由以上性质 ( 2 ) , ( 3 ) (2),(3) ,有:
    f ( i , j ) = d i , d > 1 g ( i d , j ) f(i,j)=\sum_{d\mid i,d>1}g(\frac{i}{d},j)
    g ( i , j ) = d j , d > 1 f ( i , j d ) g(i,j)=\sum_{d\mid j,d>1}f(i,\frac{j}{d})
  • 所求答案即为 f ( m , S m ) + g ( m , S m ) f(m,\frac{S}{m})+g(m,\frac{S}{m})
  • 考虑上式的组合意义,即从点 ( m , S m ) (m,\frac{S}{m}) 开始,交替将坐标的某一维除去一个非 1 1 因数,直到某一维坐标为 1 1 ,求方案数。
  • f i f_i 表示 m m 除去 i i 次非 1 1 因数后得到 1 1 的方案数, g i g_i 表示 S m \frac{S}{m} 除去 i i 次非 1 1 因数后得到 1 1 的方案数,那么考虑枚举第一次和最后一次除去了坐标的哪一维,有
    A n s = i f i g i + 1 + f i + 1 g i + 2 f i g i Ans=\sum_{i}f_ig_{i+1}+f_{i+1}g_i+2f_ig_i
  • 考虑如何计算 f i , g i f_i,g_i ,下面讨论计算 f i f_i 的方式, g i g_i 相同。
  • m = p 1 k 1 p 2 k 2 p 3 k 3 p n k n m=p_1^{k_1}p_2^{k_2}p_3^{k_3}\dots p_n^{k_n} ,考虑对 “非 1 1 因数” 的限制容斥,有
    f i = j = 1 n ( i + k j 1 i 1 ) j = 1 i 1 ( i j ) × f j f_i=\prod_{j=1}^{n}\binom{i+k_j-1}{i-1}-\sum_{j=1}^{i-1}\binom{i}{j}\times f_j
  • 时间复杂度 O ( V L o g V + L o g 2 V ) O(\frac{\sqrt{V}}{LogV}+Log^2V)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
const int MAXLOG = 205;
const int P = 998244353;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int tot, prime[MAXN], Min[MAXN];
int binom[MAXLOG][MAXLOG];
int f[MAXLOG], g[MAXLOG];
vector <int> a, b;
void sieve(int n) {
	for (int i = 0; i < MAXLOG; i++) {
		binom[i][0] = 1;
		for (int j = 1; j <= i; j++)
			binom[i][j] = (binom[i - 1][j - 1] + binom[i - 1][j]) % P;
	}
	for (int i = 2; i <= n; i++) {
		if (Min[i] == 0) prime[++tot] = Min[i] = i;
		for (int j = 1; j <= tot && prime[j] <= Min[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			Min[tmp] = prime[j];
		}
	}
}
void factor(ll n, vector <int> &a) {
	for (int i = 1; i <= tot && 1ll * prime[i] * prime[i] <= n; i++)
		if (n % prime[i] == 0) {
			int cnt = 0;
			while (n % prime[i] == 0) {
				cnt++;
				n /= prime[i];
			}
			a.push_back(cnt);
		}
	if (n != 1) a.push_back(1);
}
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int main() {
	freopen("wsm.in", "r", stdin);
	freopen("wsm.out", "w", stdout);
	sieve(1e6);
	int T; read(T);
	while (T--) {
		ll n, m; read(n), read(m);
		a.clear(), b.clear();
		if (n == m || m == 1) {
			printf("%d\n", 1);
			continue;
		}
		factor(n / m, a);
		factor(m, b);
		int lim = 100;
		for (int i = 1; i <= lim; i++) {
			int fans = 1, gans = 1;
			for (auto x : a) fans = 1ll * fans * binom[i + x - 1][i - 1] % P;
			for (auto x : b) gans = 1ll * gans * binom[i + x - 1][i - 1] % P;
			for (int j = 1; j <= i - 1; j++) {
				update(fans, P - 1ll * f[j] * binom[i][j] % P);
				update(gans, P - 1ll * g[j] * binom[i][j] % P);
			}
			f[i] = fans, g[i] = gans;
		}
		int ans = 0;
		for (int i = 1; i <= lim; i++) {
			update(ans, 2ll * f[i] * g[i] % P);
			update(ans, 1ll * f[i + 1] * g[i] % P);
			update(ans, 1ll * f[i] * g[i + 1] % P);
		}
		writeln(ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/89146962