題解 P5771【 [JSOI2016]反質數序列】
阿新 • • 發佈:2020-12-30
可以看出是道二分圖最大獨立集。
題目分析:
分析可得:
- 當兩個數奇偶性相同時,肯定滿足條件(和為偶數)
所以,源點連向偶數,奇數連向匯點(其實交換源匯點也可以,看心情叭)。如果兩數之和為質數,就在他們之間連邊。最後 $dinic$ 求最小割就 $over$ 啦。
注意點:
- 邊權均為 $1$ :所求答案為選擇的數量;
- 需要判斷 $1$ 出現的次數:兩個 $1$ 相加還是質數, 直接刪去一個 $1$ 就好了,不會影響結果。
- 兩個數連邊時,注意源點和匯點的奇偶順序,提前判斷列舉的節點的奇偶正確性。
完整程式碼:
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; #define rt register int #define int long long const int N = 6010, M = 2e6,inf = 2e9,maxn = 2e5; int n,m,S,T,tot = 1,a[N],head[N],cur[N],dep[N],f[M],prime[maxn],cnt; bool vis[maxn + 10],tru[maxn + 10]; struct node { int to,nex; }e[M]; inline void add(int x,int y,int w) { e[++tot] = (node) {y,head[x]}, f[tot] = w, head[x] = tot; e[++tot] = (node) {x,head[y]}, head[y] = tot; } inline void read(int &x) { x = 0; int ff = 1; char s = getchar(); while(s < '0' || s > '9') {s = getchar();} while(s <= '9' && s >= '0') { x = x * 10 + s - '0'; s = getchar();} x *= ff; } inline bool bfs() { memset(dep,-1,sizeof(dep)); dep[S] = 0, cur[S] = head[S]; queue<int> q; q.push(S); int now,ver; while(!q.empty()) { now = q.front(); q.pop(); for(rt i = head[now]; i; i = e[i].nex) { ver = e[i].to; if(dep[ver] == -1 && f[i]) { dep[ver] = dep[now] + 1, cur[ver] = head[ver]; if(ver == T) return 1; q.push(ver); } } } return 0; } inline int find(int x,int limit) { if(x == T) return limit; int ver,flow = 0,tmp; for(rt i = cur[x]; i && flow < limit; i = e[i].nex) { cur[x] = i, ver = e[i].to; if(dep[ver] == dep[x] + 1 && f[i]) { tmp = find(ver,min(limit - flow,f[i])); if(!tmp) dep[ver] = -1; f[i] -= tmp, f[i ^ 1] += tmp, flow += tmp; } } return flow; } inline int dinic() { int res = 0,flow; while(bfs()) while(flow = find(S,inf)) res += flow; return res; } inline void init() {//預處理質數 for(rt i = 2; i <= maxn; i ++) { if(!vis[i]) { prime[++cnt] = i; tru[i] = 1; } for(rt j = 1; j <= cnt && i *prime[j] <= maxn; j ++) { vis[i *prime[j]] = 1; if(i % prime[j] == 0) break; } } } signed main() { init(); read(n); S = n + 1, T = S + 1; int u,flag = 0,ans; for(rt i = 1; i <= n; i ++) { read(a[i]); if(a[i] == 1 && flag) {//如果出現2個及以上的1,直接刪除 n --, i --; continue; } if(a[i] == 1) flag = 1; if(a[i] & 1) add(i,T,1); else add(S,i,1); } for(rt i = 1; i <= n; i ++) { if(a[i] & 1) continue; //與源點相連的應該是偶數,奇數跳過 for(rt j = 1; j <= n; j ++) { if(a[j] % 2 == 0) continue;//參考上文 if(tru[a[i] + a[j]]) add(i,j,1); } } ans = n; printf("%lld",ans - dinic()); return 0; }