\(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;
}