Solution - Atcoder ABC263G Erasing Prime Pairs

rizynvu發表於2024-10-24

\(a_i\) 和質數看著並沒有很特殊,於是一些貪心或者是 DP 的大抵是做不了的。

於是一個想法是對於 \(a_i + a_j = p(p\in \mathbf{P})\)\((i, j)\) 連邊,然後跑網路流。
但是需要確定的是這個建出來的圖能不能拿來跑。

能夠發現的是對 \(p\in \mathbf{P}, p\not = 2\),都滿足 \(p\bmod 2 = 1\),這說明這些和為 \(p\) 的連邊都是由 \(a_i\bmod 2 = 0\)\(a_i\bmod 2 = 1\) 連上的,那麼顯然為一個二分圖。

於是就可以建圖了。
對於 \(a_i\bmod 2 = 1\),連邊 \((\operatorname{S}, i, b_i)\);對於 \(a_i\bmod 2 = 0\),連邊 \((i, \operatorname{T}, b_i)\)
對於 \(a_i\bmod 2 = 1, a_j\bmod 2 = 0, a_i + a_j= p(p\in \mathbf{P})\),連邊 \((i, j, \operatorname{inf})\)

注意到其實還有 \(p = 2\) 的特殊情況。
但時發現此時只有可能是 \((1, 1)\)
於是可以最後跑完看下 \(1\) 還有多少流量沒用,就可以計算了(因為用 \(1\)\(1\) 顯然比 \(2\)\(1\) 優)。

但是一個問題是如果直接最大流可能會導致沒有先去配對 \((a_i, a_j)(a_i\not = 1)\) 而是去配對了 \((1, a_j)\)
這可能導致 \(1\) 剩餘流量變少而使答案變小。

於是可以先不把 \(1\) 加入圖中跑一次最大流,此時就已經保證了非 \(1\) 的奇數已經儘量匹配完了。
然後再加入 \(1\),再跑一次最大流,讓 \(1\) 去匹配。
最後再統計流量即可。

時間複雜度 \(\mathcal{O}(V + \operatorname{flow}(n, n^2))\)

#include<bits/stdc++.h>
using ll = long long;
constexpr ll inf = 1e18;
int N, S, T;
namespace flow {
   static const int maxn = 1e2 + 10, maxm = 2e4 + 10;
   ll val[maxm * 2];
   int to[maxm * 2], nxt[maxm * 2], fir[maxn], tot = 1;
   inline void add(int x, int y, ll w, bool f = 1) {
      to[++tot] = y, val[tot] = w, nxt[tot] = fir[x], fir[x] = tot;
      if (f) add(y, x, 0, 0);
   }
   int hd[maxn], dep[maxn];
   inline bool bfs() {
      for (int i = 1; i <= N; i++)
         hd[i] = fir[i], dep[i] = -1;
      dep[S] = 0;
      std::queue<int> Q; Q.push(S);
      while (! Q.empty()) {
         int u = Q.front(); Q.pop();
         if (u == T) return true;
         for (int i = hd[u]; i; i = nxt[i])
            if (dep[to[i]] == -1 && val[i])
               dep[to[i]] = dep[u] + 1, Q.push(to[i]);
      }
      return false;
   }
   inline ll dfs(int u, ll fl) {
      if (u == T) {
         return fl;
      }
      ll ud = 0;
      for (int &i = hd[u]; i; i = nxt[i])
         if (dep[u] + 1 == dep[to[i]] && val[i]) {
            ll k = dfs(to[i], std::min(fl - ud, val[i]));
            if (! k) dep[to[i]] = -1;
            ud += k, val[i] -= k, val[i ^ 1] += k;
            if (ud == fl)
               return fl;
         }
      return ud;
   }
   inline ll Dinic() {
      ll ans = 0, f;
      while (bfs()) {
         while ((f = dfs(S, inf)) > 0) {
            ans += f;
         }
      }
      return ans;
   }
};
const int maxn = 1e2 + 10, maxV = 2e7 + 10, V = 2e7;
std::bitset<maxV> f;
int n;
int a[maxn], b[maxn];
int main() {
   for (int i = 2; i <= V; i++) {
      if (! f[i]) {
         for (int j = i + i; j <= V; j += i) {
            f[j] = 1;
         }
      }
   }
   scanf("%d", &n);
   for (int i = 1; i <= n; i++) {
      scanf("%d%d", &a[i], &b[i]);
   }
   S = n + 1, N = T = n + 2;
   for (int i = 1; i <= n; i++) {
      if (a[i] == 1) continue;
      if (a[i] & 1) {
         flow::add(S, i, b[i]);
      } else {
         flow::add(i, T, b[i]);
      }
   }
   for (int i = 1; i <= n; i++) {
      if (a[i] == 1) continue;
      if (a[i] & 1) {
         for (int j = 1; j <= n; j++) {
            if (j != i && ! f[a[i] + a[j]]) {
               flow::add(i, j, inf);
            }
         }
      }
   }
   ll ans = flow::Dinic();
   for (int i = 1; i <= n; i++) {
      if (a[i] == 1) {
         for (int j = 1; j <= n; j++) {
            if (j != i && ! f[a[j] + 1]) {
               flow::add(i, j, inf);
            }
         }
         flow::add(S, i, b[i]);
         ans += flow::Dinic();
         ans += flow::val[flow::tot - 1] / 2;
      }
   }
   printf("%lld\n", ans);
   return 0;
}

相關文章