LOJ #2707. 「BalticOI 2015」拔河
LOJ #2707. 「BalticOI 2015」拔河
考虑构造一个二分图 ,左右各 个点,左边表示人,右边表示位置,人对应位置连边。那么图 的左侧的 个点的度数均为 。对于这样的二分图 ,有一个很漂亮的结论:
结论 二分图 的左右两侧点数相等均为 ,如果满足左侧每个点的度数为 ,且右侧每个点的度数均不为 ,则 是基环外向树森林。
我们现在假设输入存在一个合法的匹配,即二分图 是基环外向树森林。容易发现每一棵基环外向树的贡献是独立的,且一定有 种不同的贡献。我们假设 由 棵基环外向树组成,第 棵的两个贡献为 。那么问题就变成了是否能找到序列 满足 。
设 条件即为 是一个 背包问题。直接使用 bitset
优化 背包的复杂度是 。
注意到值域 最大是 的,因此可以使用多重背包优化背包的trick:假设 则有 即 中最多会有 个不同的数。使用二进制分组+bitset
优化多重背包,可以将时间复杂度做到 ,可以通过。
参考答案
#include <bits/stdc++.h>
using namespace std;
static constexpr int Maxn = 6e4 + 5, MaxN = 1.2e5 + 5;
static constexpr int MaxK = 1.2e6 + 5, K = 1.2e6, LOG = 20;
int n, k, ch[MaxN][2], w[MaxN], en, head[Maxn];
struct Edge { int to, nxt; } e[Maxn * 2];
void add_edge(int u, int v) { e[++en] = (Edge){v, head[u]}, head[u] = en; }
int col[Maxn], stk[Maxn], top, cn, c[Maxn], coln;
bool incyc[Maxn], instk[Maxn], vis[Maxn], ct[Maxn];
void backof(int u) { do c[++cn] = stk[top]; while (stk[top--] != u); }
void dfs(int u, int fa, const int &color) {
col[u] = color, stk[++top] = u, instk[u] = true;
for (int i = head[u], to, v; i; i = e[i].nxt) {
to = e[i].to, v = ch[to][ch[to][0] == u];
if (to == fa) continue;
if (col[v] == 0) dfs(v, to, color);
else if (instk[v]) backof(v);
} top -= (stk[top] == u), instk[u] = false;
} // dfs
void dfs1(int u, int fa, int &W) {
W += w[fa] * (u <= n ? 1 : -1);
for (int i = head[u], to, v; i; i = e[i].nxt) {
to = e[i].to, v = ch[to][ch[to][0] == u];
if (to == fa) continue; dfs1(v, to, W);
}
} // dfs1
int b[Maxn][2], bw[Maxn], tot, tw[Maxn], tn;
int buc_pool[MaxK * 2], *buc = buc_pool + MaxK;
bitset<MaxK * 2> f;
int main(void) {
scanf("%d%d", &n, &k); cn = 0;
for (int i = 1; i <= n * 2; ++i)
scanf("%d%d%d", &ch[i + 2 * n][0], &ch[i + 2 * n][1], &w[i + 2 * n]),
add_edge(ch[i + 2 * n][0] += 0, i + 2 * n),
add_edge(ch[i + 2 * n][1] += n, i + 2 * n);
for (int i = 1; i <= n * 2; ++i) if (head[i] == 0) exit(puts("NO") & 0);
for (int i = 1; i <= n * 2; ++i) col[i] = 0, incyc[i] = false, ct[i] = false;
for (int i = 1; i <= n * 2; ++i)
if (e[head[i]].nxt == 0 && col[i] == 0) dfs(i, 0, ++coln), ct[cn] = true;
for (int i = 1; i <= n * 2; ++i)
if (col[i] == 0) dfs(i, 0, ++coln), ct[cn] = true;
for (int i = 1; i <= cn; ++i) incyc[c[i]] = true;
for (int i = 1; i <= cn; ++i)
for (int u = c[i], j = head[u], v, to; j; j = e[j].nxt) {
to = e[j].to, v = ch[to][ch[to][0] == u];
if (!incyc[v]) dfs1(v, to, bw[col[u]]);
} for (int i = 1; i <= coln; ++i) b[i][0] = bw[i], b[i][1] = bw[i];
memset(vis, false, sizeof(vis));
for (int l = 1, r, co, wc; l <= cn; l = r + 1) {
for (r = l; !ct[r]; ++r); co = col[c[l]], wc = 0;
if (r == l + 1) {
for (int u = c[l], j = head[u], x = 0, to, v; j; j = e[j].nxt) {
to = e[j].to, v = ch[to][ch[to][0] == u];
if (incyc[v]) wc += (x == 0 ? w[to] : -w[to]), x ^= 1;
}
} else for (int i = l, u, nu; i <= r; ++i) {
u = c[i], nu = c[(i - l + 1) % (r - l + 1) + l];
for (int j = head[u], to, v; j; j = e[j].nxt) {
to = e[j].to, v = ch[to][ch[to][0] == u];
if (v == nu) wc += w[to] * (u <= n ? 1 : -1);
}
} b[co][0] += wc, b[co][1] -= wc;
} for (int i = 1; i <= coln; ++i) tot += b[i][0];
for (int i = 1; i <= coln; ++i) bw[i] = b[i][1] - b[i][0];
for (int i = 1; i <= coln; ++i) buc[bw[i]]++;
for (int i = -K, j; i <= K; ++i) if (buc[i]) {
for (j = 0; (1 << j + 1) - 1 <= buc[i]; ++j) tw[++tn] = i * (1 << j);
tw[++tn] = i * (buc[i] - (1 << j) + 1);
}
f.set(tot + K);
for (int i = 1; i <= tn; ++i)
if (tw[i] > 0) f |= f << tw[i];
else f |= f >> (-tw[i]);
bool ans = false;
for (int i = -k; i <= k; ++i) ans |= f[i + K];
puts(ans ? "YES" : "NO");
exit(EXIT_SUCCESS);
} // main
标签:
OI—动态规划—背包
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】