[建模] [二分图] [贪心] [ARC080F] Prime Flip _ noip模拟赛 疯癫兄弟

posted on 2023-11-15 00:46:57 | under | source

注:当初 noip 模拟赛的题,赛后听完评讲觉得挺好,遂记之。恰好夏令营时 lby 学长讲到了这道题,所以翻出来水一发题解回顾一下。

巧妙的建模,还需结合贪心。

思路

首先用 \(0/1\) 表示状态,那么每次操作等价于给区间异或 \(1\),可以考虑差分在首尾打上标记。这样就将区间操作转化为对数对操作。

既然考虑了差分,那么不妨把输入也看成差分操作,即 \(f_{x_i} \bigoplus1,f_{x_{i+1}} \bigoplus1\)

注意到对 \(0\) 端点操作一定不优,那么首先对操作数对的距离进行讨论。

  1. 为奇质数:花费 \(1\)
  2. 为偶数:由哥德巴赫猜想知可拆为两个奇质数操作,花费 \(2\)
  3. 为非质奇数:花费 \(3\)

(注意保证有解)

然后可以由奇偶性建二分图,显然有个贪心,先进行操作 \(1\),然后是操作 \(2\),最后实在不行再用操作 \(3\)

因此可以按如下步骤操作:

  1. 优先连奇质数边,即跑一边二分图最大匹配。
  2. 剩下的未匹配点内部连偶数边。
  3. 显然左右部都至多剩下 \(1\) 个点,对它们连非质奇数边即可。

怎么严谨证明呢?我们设 \(A,B,x\) 分别为二分图左右部顶点数,以及操作 \(1\) 的个数(即匹配边数量)。

\(G(x)\) 表示此时花费,有 \(G(x)=x+2\lfloor \frac {A-x}2 \rfloor+2\lfloor \frac {B-x}2\rfloor+3((A-x)\bmod 2)\)

然后对 \(x\) 分奇偶讨论,容易发现 \(G(x)\) 是单调的,所以取 \(x\) 最大值即可。

时间开销最大的是二分图最大匹配部分,当初考场的毒瘤出题人卡了匈牙利算法,所以用的是 \(\rm dinic\)

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 2e3 + 5, M = 1e6 + 2e3 + 5, C = 1e7, inf = 1e9; 
int k, n, x, a[N], s, t, L, R;
int head[N], now[N], tot = 1;
int dep[N], ans;
bool yfz[C + 5];
struct edge{int v, nxt, w;}e[M << 1];

inline void add(int u, int v, int w) {e[++tot] = {v, head[u], w}, head[u] = tot;}
inline bool bfs(){
	for(int i = s; i <= t;++i) dep[i] = inf, now[i] = head[i];
	queue<int>q;
	q.push(s), dep[s] = 0;
	while(!q.empty()){
		int u = q.front(); q.pop();
		for(int i = head[u], v; v = e[i].v, i; i = e[i].nxt)
			if(e[i].w && dep[v] == inf){
				dep[v] = dep[u] + 1;
				q.push(v);
				if(v == t) return true;
			}
	}
	return false;
}
inline int dfs(int u, int sum){
	if(u == t) return sum;
	int k, res = 0;
	for(int i = now[u], v; v = e[i].v, i && sum; now[u] = i, i = e[i].nxt)
		if(e[i].w && dep[u] + 1 == dep[v]){
			k = dfs(v, min(sum, e[i].w));
			if(!k) dep[v] = inf;
			e[i].w -= k, e[i ^ 1].w += k;
			sum -= k, res += k;
		}
	return res; 
}
inline bool check(int p){
	if(p <= 2 || p % 2 == 0) return false;
	for(int i = 2; i * i <= p;++i)
		if(p % i == 0) return false;
	return true;
}
inline void build(){
	cin >> k;
	for(int i = 1; i <= k; ++i) scanf("%d", &x), yfz[x] ^= 1, yfz[x + 1] ^= 1;
	for(int i = 0; i <= C + 1; ++i) if(yfz[i]) a[++n] = i;
	s = 0, t = n + 1;
	for(int i = 1; i <= n; ++i){
		if(a[i] & 1) add(s, i, 1), add(i, s, 0), ++L;
		else add(i, t, 1), add(t, i, 0), ++R;
		for(int j = i + 1; j <= n; ++j)
			if(check(a[j] - a[i]))
				if(a[i] & 1) add(i, j, 1), add(j, i, 0);
				else add(j, i, 1), add(i, j, 0);
	}
}
inline void solve(){
	while(bfs()) ans += dfs(s, inf); 
	int pipei = ans;
	ans += (L - pipei) / 2 * 2 + (R - pipei) / 2 * 2;
	if((L - pipei) & 1) ans += 3;
}
signed main(){
	build();
	solve();
	cout << ans;
	return 0;
} 
posted @ 2026-01-12 20:14  Zwi  阅读(2)  评论(0)    收藏  举报