Loading

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)\) 为依次钦定第一次相交位置翻转所得到的方案数。

\[ans=f(\varnothing)-f(L)-f(R)+f(LR)+f(RL)-f(LRL)-f(RLR)\cdots \]

对于某种不合法方案 LRLRLRL (与某条线多次相交是只钦定第一次的),

则对于第一个 L 和 R 开始的每个前缀 \(S\) ,都有 \((-1)^{\left|S\right|}\) 的贡献,算上 \(\varnothing\) ,刚好是 \(0\)

所以上述做法是正确的。

将式子写出来:

\[\begin{align*} &&\vdots\\ &L &(m-2\ell+r,n+2\ell-r) & &-{n+m,\choose n+2\ell-r}\\ &R &(n+\ell-r,m-\ell+r) &&{n+m,\choose n+\ell-r}\\ &L &(m-\ell,n+\ell)&&-{n+m,\choose n+\ell}\\ &\varnothing &(n,m) &&{n+m\choose n}\\ &R &(m-r,n+r)&&-{n+m\choose n+r}\\ &L &(n-\ell+r, m+\ell-r)&&{n+m\choose n-\ell+r}\\ &R &(m+\ell-2r, n-\ell+2r)&&-{n+m\choose n-\ell+2r}\\ &&\vdots \end{align*} \]

得到

\[Ans=\sum_{k\in\mathbb Z} {n+m\choose n+k(\ell-r)}-{n+m\choose n+k(\ell-r)+\ell} \]

此题结。

点击查看

#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;
}

posted @ 2025-11-07 22:11  qkhm  阅读(5)  评论(0)    收藏  举报