[建模] [二分图] [贪心] [ARC080F] Prime Flip | noip模拟赛 疯癫兄弟
注:当初 noip 模拟赛的题,赛后听完评讲觉得挺好,遂记之。恰好夏令营时 lby 学长讲到了这道题,所以翻出来水一发题解回顾一下。
巧妙的建模,还需结合贪心。
思路
首先用
既然考虑了差分,那么不妨把输入也看成差分操作,即
注意到对
- 为奇质数:花费
1 。 - 为偶数:由哥德巴赫猜想知可拆为两个奇质数操作,花费
2 。 - 为非质奇数:花费
3 。
(注意保证有解)
然后可以由奇偶性建二分图,显然有个贪心,先进行操作
因此可以按如下步骤操作:
- 优先连奇质数边,即跑一边二分图最大匹配。
- 剩下的未匹配点内部连偶数边。
- 显然左右部都至多剩下
1 个点,对它们连非质奇数边即可。
怎么严谨证明呢?我们设
令
然后对
时间开销最大的是二分图最大匹配部分,当初考场的毒瘤出题人卡了匈牙利算法,所以用的是
代码
#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;
}