题解:P13275 [NOI2025] 集合
退役好久没做题所以也没更新. 最近放假在家摆烂摸鱼, 刷 b 站看到某位主播 vp 今年 NOI, 发现还有这种魔怔数数题, 就来做着玩玩. 训 AtCoder 取模计数题的人会有好报的. 😊
给定正整数
数据范围:
考虑对
代回原式即得容斥系数是
这里
\mathrm{pc}(x) 表示x 的二进制表示中 1 的数量. 容斥系数怎么算的? 将条件表示为每个二进制位的 "相等" 条件相乘, 权值((1,0),(0,1)) 作差分得到((1,-1),(-1,2)) , 按分配律全部展开就好啦.
贡献式里每个元素的方案独立:
又因为
如果有
随便看了一下其他题解, 感觉这部分讲得比较模糊, 所以在这里也传了一份.
总结一下, 用后缀和/积计算
#include <bits/stdc++.h>
#define fi first
#define se second
#define pc __builtin_popcount
typedef std::pair<int, int> PII; // coefficient and degree of 0
typedef long long LL;
const int mod = 998244353;
int qmo(int x) { return x + (x >> 31 & mod); }
int ksm(int a, int b) {
int res = 1;
for (; b; b >>= 1, a = (LL)a * a % mod)
if (b & 1) res = (LL)res * a % mod;
return res;
}
PII operator + (const PII &a, const PII &b) {
if (a.se == b.se) return PII(qmo(a.fi + b.fi - mod), a.se);
return a.se < b.se ? a : b;
}
PII operator += (PII &a, const PII &b) { a = a + b; return a; }
PII operator - (const PII &a) { return PII(qmo(-a.fi), a.se); }
PII operator - (const PII &a, const PII &b) { return a + (-b); }
PII operator -= (PII &a, const PII &b) { a = a - b; return a; }
PII operator * (const PII &a, int b) {
if (b == 0) return PII(a.fi, a.se + 1);
return PII((LL)a.fi * b % mod, a.se);
}
PII operator *= (PII &a, int b) { a = a * b; return a; }
PII operator * (const PII &a, const PII &b) {
return PII((LL)a.fi * b.fi % mod, a.se + b.se);
}
PII operator *= (PII &a, const PII &b) { a = a * b; return a; }
void solve() {
int n; std::cin >> n;
std::vector<int> pwn2(n+1), pwi2(n+1); // power of -2 and 1/2
pwn2[0] = pwi2[0] = 1;
for (int i = 1; i <= n; ++i) {
pwn2[i] = pwn2[i-1] * (mod - 2ll) % mod;
pwi2[i] = (pwi2[i-1] + (pwi2[i-1] & 1) * mod) / 2;
}
int N = 1 << n; std::vector<PII> s(N), t(N);
for (int i = 0, x; i < N; ++i) {
std::cin >> x;
if (x == mod - 1) {
s[i] = PII(1, 1);
t[i] = PII(mod - 1, -2);
} else {
s[i] = PII(1 + x, 0);
t[i] = PII((1 + 2ll * x) * ksm(1 + x, mod - 3) % mod, 0);
}
}
for (int i = 0; i < n; ++i)
for (int j = 0; j < N; ++j)
if (!(j & (1 << i))) {
s[j] *= s[j ^ (1 << i)];
t[j] *= t[j ^ (1 << i)];
}
for (int i = 0; i < N; ++i) {
s[i] *= pwn2[pc(i)];
t[i] *= pwi2[pc(i)];
}
for (int i = 0; i < n; ++i)
for (int j = 0; j < N; ++j)
if (j & (1 << i)) s[j] += s[j ^ (1 << i)];
for (int i = 0; i < N; ++i) s[i] *= s[i];
for (int i = 0; i < n; ++i)
for (int j = 0; j < N; ++j)
if (j & (1 << i)) s[j] -= s[j ^ (1 << i)];
int ans = 0;
for (int i = 0; i < N; ++i) {
PII cur = s[i] * t[i]; assert(cur.se >= 0);
if (cur.se == 0) ans = qmo(ans + cur.fi - mod);
}
printf("%d\n", ans);
}
int main() {
int _, t;
std::ios::sync_with_stdio(false);
std::cin >> _ >> t;
while (t --) { solve(); }
}