[建模] [二分图] [贪心] [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\)。
- 为偶数:由哥德巴赫猜想知可拆为两个奇质数操作,花费 \(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;
}

浙公网安备 33010602011771号