ABC263G 题解
前言
网络流。很好的题目。
思路
数据范围不大但是又跑不了指数算法,所以考虑网络流。
对于绝大多数情况,\(a_i + a_j\) 是质数当且仅当 \(a_i\) 是奇数且 \(a_j\) 是偶数(反过来同理)。
所以有一个显然的做法:
- \(S \xrightarrow{1} a_i\ (a_i \text{ is odd})\)。
- \(a_i \xrightarrow{1} T\ (a_i \text{ is even})\)。
- \(a_i \xrightarrow{\infty} a_j\ (a_i \text{ is odd} \wedge a_j \text{ is even} \wedge (a_i + a_j) \text{ is prime})\)。
- 跑最大流即可。
唯一的反例是 \(1+1=2\)。这意味着要尽可能多地留下 \(1\)。显而易见,最小费用最大流模型,费用即 \(1\) 留下的个数。
答案即 \(\text{flow} + \left\lfloor\dfrac{\text{count of 1} - \text{cost}}2\right\rfloor\)。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 1005; const ll inf = 0x3f3f3f3f3f3f3f3f;
namespace Graph {
struct Edge {int now, nxt; ll w, cost;} e[1919810];
int head[N], cur = 1;
void ad(int u, int v, ll w, int cost)
{
e[++cur].now = v, e[cur].nxt = head[u], e[cur].w = w, e[cur].cost = cost;
head[u] = cur;
}
void add(int u, int v, ll w, int cost) {ad(u, v, w, cost), ad(v, u, 0, -cost);}
ll dis[N], icost[N]; int pre[N]; bool inque[N];
int s, t;
bool spfa()
{
queue <int> q;
memset(dis, inf, sizeof dis), memset(icost, 0, sizeof icost);
q.push(s), dis[s] = 0, icost[s] = inf, inque[s] = true;
while (!q.empty())
{
int u = q.front();
q.pop(), inque[u] = false;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].now;
if (!e[i].w) continue;
if (dis[u] + e[i].cost < dis[v])
{
dis[v] = dis[u] + e[i].cost, pre[v] = i;
icost[v] = min(icost[u], e[i].w);
if (!inque[v]) q.push(v), inque[v] = true;
}
}
}
return icost[t] > 0;
}
void EK(ll &flow, int &cost)
{
while (spfa())
{
ll w = icost[t];
flow += w, cost += w * dis[t];
for (int i = t; i != s; i = e[pre[i] ^ 1].now)
e[pre[i]].w -= w, e[pre[i] ^ 1].w += w;
}
}
}; using namespace Graph;
int val[N], cnt[N];
bool isprime(int x)
{
if (x <= 1) return false;
for (int i = 2; i * i <= x; i++)
if (x % i == 0)
return false;
return true;
}
int main()
{
int n, cnt1 = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d%d", &val[i], &cnt[i]);
if (val[i] == 1) cnt1 += cnt[i];
}
s = 0, t = n + 1;
for (int i = 1; i <= n; i++)
if (val[i] % 2 == 1) add(s, i, cnt[i], val[i] == 1);
else add(i, t, cnt[i], 0);
for (int i = 1; i <= n; i++)
if (val[i] % 2 == 1)
for (int j = 1; j <= n; j++)
if (val[j] % 2 == 0 && isprime(val[i] + val[j]))
add(i, j, inf, 0);
ll flow = 0; int cost = 0;
EK(flow, cost);
cout << flow + (cnt1 - cost) / 2;
return 0;
}
希望能帮助到大家!