LOJ #2707. 「BalticOI 2015」拔河

LOJ #2707. 「BalticOI 2015」拔河

​ 考虑构造一个二分图 G,左右各 2n 个点,左边表示人,右边表示位置,人对应位置连边。那么图 G 的左侧的 2n 个点的度数均为 2。对于这样的二分图 G,有一个很漂亮的结论:

结论 二分图 G 的左右两侧点数相等均为 n,如果满足左侧每个点的度数为 2,且右侧每个点的度数均不为 0,则 G 是基环外向树森林。

​ 我们现在假设输入存在一个合法的匹配,即二分图 G 是基环外向树森林。容易发现每一棵基环外向树的贡献是独立的,且一定有 2 种不同的贡献。我们假设 Gm 棵基环外向树组成,第 i 棵的两个贡献为 bi,0/1。那么问题就变成了是否能找到01序列 ci 满足 |ibi,ci|k

​ 设 S=ibi,0,di=bi,1bi,0 条件即为 |S+icidi|k 是一个 01背包问题。直接使用 bitset 优化 01背包的复杂度是 O(nkω)

​ 注意到值域 idi 最大是 O(k) 的,因此可以使用多重背包优化01背包的trick:假设 idiT 则有 |{di}|Tdi 中最多会有 O(T) 个不同的数。使用二进制分组+bitset优化多重背包,可以将时间复杂度做到 O(kklogkω),可以通过。

参考答案

#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
posted @   cutx64  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示