P5366 [SNOI2017] 遗失的答案
P5366 [SNOI2017] 遗失的答案 题解
题意
给定 \(n, L, G\),有 \(Q\) 组询问,每次给出一个 \(x\),求有多少的 \(\{1, 2, \dots, n\}\) 的子集,满足:
- 所有元素的最大公约数为 \(G\)。
- 所有元素的最小公倍数为 \(L\)。
- 集合包含 \(x\)。
\(n, x, L, G \le 10^8, Q \le 10^5\)
题解
好像和题解做法都不太一样?
首先把 \(n, G, L, x\) 都除以 \(G\),除去后整个结构是不变的,于是只需考虑 \(G = 1\)。
考虑刻画最小公倍数与最大公倍数。
不难想到从质因数分解的角度刻画,而简单一算,发现 \(10^8\) 以内的数最多有 \(8\) 个质因子。
另外,我们选出的数一定为 \(L\) 的约数,而 \(10^8\) 以内的数约数最多的有大概 \(700\) 个。
假如没有包含 \(x\) 的条件,只要求方案数会怎么样?
取 \(L\) 的约数集合 \(g\),同时设 \(L\) 有 \(m\) 个质因子。
只需取以 \(S \subseteq g\),且对于 \(L\) 的一个质因子 \(p\),存在 \(x \in S\),满足 \(v(p, x) = v(p, L)\),存在 \(y \in S\),满足 \(v(p, y) = 0\)。
于是由于只有 \(m\) 个质因子,我们就可以用一个 \(2m\) 位的数压下我们需要知道的所有信息。
我们记 \(F(A)\) 表示 \(A\) 的状态。
那么我们只需求 \([x^{2^{2m}-1}](1+x^{F(g_1)})(1+x^{F(g_2)})\dots(1+x^{F(g_k)})\)。
其中我们乘法是或卷积。
接下来我们考虑要求包含 \(x\) 的情况,介于 \(x\) 最多大概 \(700\) 种非零,我们不妨全部预处理出来。
我们知道,求或卷积时,我们先做DWT,然后点乘,在做IDWT。
在本题中,记 \(B = (1+x^{F(g_1)})(1+x^{F(g_2)})\dots(1+x^{F(g_{i-1})})(1+x^{F(g_{i+1})})\dots(1+x^{F(g_k)})\)。
由于 \(DWT(1 + x^k)\) 这样的式子每一项都属于 \(\{1, 2\}\),于是 \(DWT(B)\) 完全可以在 \(2^m\) 的时间内算出来。
然后简单推一下式子,记 \(C = DWT(B)\),我们要求:
可以在 \(\mathcal O(2^{2m})\) 内算出来,于是总时间是 \(\mathcal O(|g|2^{2m})\),大约在 \(5 \times 10^7\) 左右,可以接受。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int K = 1005, N = (1 << 16);
const LL P = 1e9 + 7, iv2 = (P + 1) >> 1;
int n, G, L, cnt[N];
int num[K], tot, a[K], cur[K], id, st[K]; LL pw[N];
unordered_map<int, int> ans;
int v(int p, int n) { int cnt = 0; while (n % p == 0) n /= p, cnt++; return cnt; }
void init(int g) {
for (int i = 1; i * i <= g; i++)
if (g % i == 0) { num[++tot] = i; if (i * i < g) num[++tot] = g / i; }
sort(num + 1, num + tot + 1);
while (num[tot] > n) tot--;
for (int i = 2; i * i <= g; i++)
if (g % i == 0) { a[++id] = i; while (g % i == 0) cur[id]++, g /= i; }
if (g != 1) a[++id] = g, cur[id] = 1;
pw[0] = 1;
for (int i = 1; i <= tot; i++) {
pw[i] = pw[i - 1] * 2 % P;
for (int j = 1; j <= id; j++) st[i] = (st[i] << 1) | (cur[j] == v(a[j], num[i]));
for (int j = 1; j <= id; j++) st[i] = (st[i] << 1) | (0 == v(a[j], num[i]));
}
for (int i = 1; i < (1 << (2 *id)); i++)
cnt[i] = cnt[i - (i & -i)] + 1;
}
LL A[N], B[N];
void IDMT(LL *A, int n) {
for (int i = 1; i < (1 << n); i *= 2)
for (int j = 0; j < (1 << n); j += i << 1)
for (int k = 0; k < i; k++)
(A[i + j + k] += P - A[j + k]) %= P;
}
int main() {
scanf("%d%d%d", &n, &G, &L);
if (L % G != 0) { int q; scanf("%d", &q); for (; q--; ) puts("0"); return 0; }
n /= G, L /= G, init(L);
for (int i = 1; i <= tot; i++)
for (int j = 0; j < 1 << (2 * id); j++)
A[j] += (st[i] | j) == j;
for (int i = 1; i <= tot; i++) {
for (int j = 0; j < 1 << (2 * id); j++)
B[j] = A[j] - ((st[i] | j) == j), B[j] = pw[B[j]];
LL res = 0;
for (int j = 0; j < (1 << 2 * id); j++)
if ((j | st[i]) == j) res += B[j] * (cnt[j] & 1 ? (-1) : (1)), res = (res + P) % P;
ans[num[i]] = res;
}
int q; scanf("%d", &q);
for (int x; q--; ) scanf("%d", &x), printf("%d\n", x % G == 0 ? ans[x / G] : 0);
return 0;
}

浙公网安备 33010602011771号