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

希望能帮助到大家!

posted @ 2023-06-17 10:18  liangbowen  阅读(41)  评论(0编辑  收藏  举报