2025.10.22
B.
将 \(0\) 看作 \(-1\) ,\(1\) 看作 \(1\) ,则合法当且仅当前缀和序列的极差不超过 \(k\) 。
先说模型转化,填 \(0/1\) 可以看作在网格图上走,向右表示填 \(0\) ,向上表示填 \(1\) ,问走到 \((x, y)\) 的合法方案。
上述限制就可以看作有两条线,走的时候不能超过这两条线。
枚举上界,单步容斥要求一定接触但不超过上界,然后就得到了双线反射容斥的经典子问题。
先来讲单线反射容斥:
不能接触 \(y=x+\ell(\ell\ne 0)\) 这条线,求 \((0, 0)\to (n, m)\) 的方案数(只能向右或向上走)
不考虑不合法的情况,方案数为 \({n+m\choose m}\) ,考虑哪些方案不合法:
发现每一种不合法的情况,取第一次与限制线相交的位置,之后的部分沿着线翻折,一定与一条 \((0, 0)\to (m-\ell, n+\ell)\) 的路径构成双射。
单步容斥得到 \(ans={n+m\choose m}-{n+m\choose n+\ell}\) 。
扩展一下得到双线反射容斥:
不能接触 \(y=x+\ell\) 和 \(y=x+r\) \((\ell <0<r)\) 两条线,求 \((0, 0)\to (n, m)\) 方案数(只能向右或向上走)
记 \(f(S)\) 为依次钦定第一次相交位置翻转所得到的方案数。
则
对于某种不合法方案 LRLRLRL (与某条线多次相交是只钦定第一次的),
则对于第一个 L 和 R 开始的每个前缀 \(S\) ,都有 \((-1)^{\left|S\right|}\) 的贡献,算上 \(\varnothing\) ,刚好是 \(0\) 。
所以上述做法是正确的。
将式子写出来:
得到
此题结。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = (a), ed##i = (b); i <= ed##i; ++i)
#define rep(i, a, b) for (int i = (a), ed##i = (b); i >= ed##i; --i)
#define il inline
#define arr(ty, tn) std::array<ty, tn>
#define gmx(a, b) a = std::max(a, b)
#define gmn(a, b) a = std::min(a, b)
template <typename T>
void _debug(const T& t) { std::cerr << t << '\n'; }
template <typename T, typename... Args>
void _debug(const T& t, const Args&...res) { std::cerr << t << ' '; _debug(res...); }
#define debug(...) _debug(#__VA_ARGS__ " =", __VA_ARGS__)
const int LN = 5e7 + 7;
const int mod = 998244353;
typedef long long ll;
typedef std::pair<int, int> PII;
bool FIRPOS;
int x, y, k, fac, inv[LN], L, Com[LN];
bool ENDPOS;
il int add(int u, int v) { return u + v >= mod ? u + v - mod : u + v; }
il void upa(int&u,int v) { u = add(u, v); }
il int mul(ll u, ll v) { return u * v >= mod ? u * v % mod : u * v; }
il void upm(int&u, int v) { u = mul(u, v); }
il int MyPow(int a, int b) { int ans = 1; for (; b; b >>= 1, upm(a, a)) if (b & 1) upm(ans, a); return ans; }
il int C(int n, int m) { if (m < 0 or n < m) return 0; return Com[m]; }
int S(int l, int r) {
if (y >= x + r or y <= x + l or l == r) return 0;
int R = (x + r) / (r - l), ans = 0;
lep(i, L, R) upa(ans, add(C(x + y, x - i * (r - l)), mod - C(x + y, x - i * (r - l) + r)));
return ans;
}
int main() {
std::ios::sync_with_stdio(false),
std::cin.tie(nullptr), std::cout.tie(nullptr);
std::cin >> x >> y >> k;
fac = 1; lep(i, 2, x + y) upm(fac, i);
inv[x + y] = MyPow(fac, mod - 2);
rep(i, x + y, 1) inv[i - 1] = mul(inv[i], i);
lep(i, 0, x + y) Com[i] = mul(mul(fac, inv[i]), inv[x + y - i]);
int ans = 0; L = -y / (k + 1);
lep(mx, 0, k) upa(ans, add(S(mx - k - 1, mx + 1), mod - S(mx - k - 1, mx)));
std::cout << ans << '\n';
return 0;
}
D.
2-SAT 建图。
每个命题 \(i\) 都有一个其反命题 \(mut_i\) ,处理出传递闭包,如果可以相互到达则无合法方案。
如果 \(i\) 可以到达 \(mut_i\) ,则 \(mut_i\) 自动成立,不需要再处理。
然后对于剩下的点,先将不同的连通块分开,贡献相乘。
然后从剩下的点中度数最大的随机一个枚举其状态。
如果某个命题成立,则其能到达的命题就全部成立;否则,能到达它的命题全部不成立。
枚举之后可能会出现新的相互独立的连通块,重复这个过程知道所有点的状态已经确定,处理答案。
因为随机化以及选度数最大的点,很难卡满。
笔者不会分析复杂度,据说是 \(\mathcal O(1.38^n)\) 。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = (a), ed##i = (b); i <= ed##i; ++i)
#define rep(i, a, b) for (int i = (a), ed##i = (b); i >= ed##i; --i)
#define il inline
#define arr(ty, tn) std::array<ty, tn>
#define gmx(a, b) a = std::max(a, b)
#define gmn(a, b) a = std::min(a, b)
template <typename T>
void _debug(const T& t) { std::cerr << t << '\n'; }
template <typename T, typename... Args>
void _debug(const T& t, const Args&...res) { std::cerr << t << ' '; _debug(res...); }
#define debug(...) _debug(#__VA_ARGS__ " =", __VA_ARGS__)
const int LN = 200 + 7;
typedef long long ll;
typedef std::vector <int> vec;
typedef std::pair<int, int> PII;
bool FIRPOS;
int n, m, bel[LN], tot, d[LN], mut[LN];
bool vis[LN], E[LN][LN], G[LN][LN];
vec e[LN], g[LN];
bool ENDPOS;
il void add(int u, int v) { e[u].emplace_back(v), g[v].emplace_back(u); }
void dfs1(int u) { vis[u] = true; for (int v : e[u]) if (!vis[v]) dfs1(v); d[++*d] = u; }
void dfs2(int u) { bel[u] = tot; for (int v : g[u]) if (!bel[v]) dfs2(v); }
void col(int u, vec& nw) {
vis[u] = true;
for (int v : nw) if (!vis[v] and (G[u][v] or G[v][u])) col(v, nw);
}
std::mt19937 rd(time(0));
ll sol(vec nw) {
if (!nw.size()) return 1;
std::shuffle(nw.begin(), nw.end(), rd);
col(nw[0], nw), col(mut[nw[0]], nw);
bool g = false;
for (int v : nw) if (!vis[v]) { g = true; break; }
if (g) {
vec p0, p1;
for (int v : nw) if (vis[v]) p1.emplace_back(v), vis[v] = false; else p0.emplace_back(v);
return sol(p0) * sol(p1);
}
int mx = -1, u = 0, t;
for (int v : nw) { vis[v] = false, t = 0;
for (int k : nw) if (E[v][k] or E[k][v]) ++t;
if (t > mx) mx = t, u = v;
}
vec p0, p1;
for (int v : nw) if (v != u and v != mut[u]) {
if (!G[u][v] and !G[v][mut[u]]) p0.emplace_back(v);
if (!G[mut[u]][v] and !G[v][u]) p1.emplace_back(v);
}
return sol(p0) + sol(p1);
}
int main() {
std::ios::sync_with_stdio(false),
std::cin.tie(nullptr), std::cout.tie(nullptr);
int c1 = clock(), a, x, b, y;
std::cin >> n >> m;
lep(i, 1, m) std::cin >> a >> x >> b >> y, add(x + n * a, y + n * !b), add(y + n * b, x + n * !a);
lep(i, 1, 2 * n) if (!vis[i]) dfs1(i);
rep(i, 2 * n, 1) if (!bel[d[i]]) ++tot, dfs2(d[i]);
lep(i, 1, n) {
if (bel[i] == bel[i + n]) return std::cout << "0\n", 0;
mut[bel[i]] = bel[i + n], mut[bel[i + n]] = bel[i], vis[i] = vis[i + n] = false;
for (auto v : e[i]) if (bel[i] != bel[v]) E[bel[i]][bel[v]] = G[bel[i]][bel[v]] = true;
for (auto v : e[i + n]) if (bel[i + n] != bel[v]) E[bel[i + n]][bel[v]] = G[bel[i + n]][bel[v]] = true;
}
lep(k, 1, tot) lep(i, 1, tot) lep(j, 1, tot) G[i][j] |= G[i][k] & G[k][j];
vec p;
lep(i, 1, tot) if (G[i][mut[i]]) vis[i] = vis[mut[i]] = true;
lep(i, 1, tot) if (!vis[i]) p.emplace_back(i);
std::cout << sol(p) << '\n';
std::cerr << clock() - c1 << " ms " << std::fabs(&ENDPOS - &FIRPOS) / 1024 / 1024 << " MB\n";
return 0;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

双线反射容斥+搜索
浙公网安备 33010602011771号