###Description 靈夢和魔理沙即將展開彈幕大戰,爲了提升實力,靈夢打算突擊學習一些符卡。每張符卡有三個屬性:火力、時長和等級。求勝心切的靈夢但願在一次戰鬥中,使用的符卡的火力值總和不小於 $k$ 。然而,因爲一些特殊的緣由,若是兩張符卡的時長之和是一個質數,那麼它們便不能在一次戰鬥中同時使用。此外,若是符卡的等級超過了靈夢的等級,她也沒法學習這張符卡。因爲提高等級是一件很困難的事情,靈夢想要知道,爲了達到目標,本身所需的最低等級是多少。c++
###Input 第一行兩個整數 $n, k$ ,表示可供學習的符卡數量,以及靈夢但願達到的火力總和。git
接下來 $n$ 行,每行三個整數 $p_i, t_i, l_i$ 分別表示一張符卡的火力、時長和等級。學習
###Output 一行一個整數,表示靈夢所需的最低等級。若是找不到知足要求的一組符卡,輸出 $−1$ 。spa
###Sample Inputcode
5 8 5 5 1 1 5 4 4 6 3 1 12 4 3 12 1
###Sample Outputip
4
###Solution 最小割。ci
首先二分一下等級,接下來全部不知足等級條件的卡所有忽略。get
而後建圖。若 $t[x]$ 爲奇數,則 $S$ 向 $x$ 連一條流量 $p[x]$ 的邊。若 $t[x]$ 爲偶數,則 $x$ 向 $T$ 連一條流量 $p[x]$ 的邊。若 $t[x]$ 爲奇數且 $t[y]$ 爲偶數且 $t[x] + t[y]$ 爲質數,則 $x$ 往 $y$ 連一條流量 $INF$ 的邊。能夠腦補出來, $t$ 爲奇數的點全在「左邊」, $t$ 爲偶數的點全在右邊,且僅在左右之間有邊(連着 $S$ 和 $T$ 的除外)。it
爲何這樣建圖呢?首先中間的 $INF$ 邊是假設不合法的卡都能取,而後跑最小割來捨棄。並且咱們知道奇數+奇數、偶數+偶數都等於偶數,不多是質數,因此左邊內部和右邊內部不可能有邊,因此這樣建圖。io
可是!$2$ 是惟一的偶質數,因此咱們須要特判掉 $1$ 。由於選出的卡中至多隻有一張 $t=1$ 的卡,因此在符合等級限制中的卡中選一個 $t=1$ 且 $p$ 最大的卡就行了,其它的所有忽略。
最後...
#include<bits/stdc++.h> using namespace std; #define N 2001 #define INF 2000000000 #define rep(i, a, b) for (int i = a; i <= b; i++) #define ll long long inline int read() { int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); } while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag; } int n, K; struct card { int p, t, l; inline void in() { p = read(); t = read(); l = read(); } }a[N]; int S, T; struct edge { int v, c, next; }e[500001]; int head[N], tot = 1; int q[N], dep[N]; inline void insert(int u, int v, int c) { tot++; e[tot].v = v, e[tot].c = c, e[tot].next = head[u]; head[u] = tot; } inline void add(int u, int v, int c) { insert(u, v, c), insert(v, u, 0); } inline bool bfs() { memset(dep, 0, sizeof dep); dep[S] = 1; int l = 1, r = 1; q[1] = S; while (l <= r) { int u = q[l++]; for (int i = head[u], v; i; i = e[i].next) if (e[i].c && !dep[v = e[i].v]) { dep[v] = dep[u] + 1, q[++r] = v; if (!(v ^ T)) return 1; } } return 0; } int dfs(int u, int dist) { if (!(u ^ T) || !dist) return dist; int ret = 0; for (int i = head[u], v, c; i; i = e[i].next) if ((c = e[i].c) && !(dep[v = e[i].v] ^ (dep[u] + 1))) { int d = dfs(v, min(dist, c)); dist -= d, ret += d, e[i].c -= d, e[i ^ 1].c += d; if (!dist) break; } return ret; } ll prime[1000001]; int cnt; bool notPrime[1000001], hasEdge[N][N]; inline void makePrime() { notPrime[1] = 1; int n = 1000000, m = sqrt(n + 0.5); rep(i, 2, m) if (!notPrime[i]) for (int j = i * i; j <= n; j += i) notPrime[j] = 1; rep(i, 1, n) if (!notPrime[i]) prime[++cnt] = i; } inline bool checkPrime(ll x) { rep(i, 1, cnt) { if (prime[i] * prime[i] > x) break; if (x % prime[i] == 0) return 0; } return 1; } inline void init() { rep(i, 1, n) rep(j, i + 1, n) if ((a[i].t + a[j].t <= 1000000 && !notPrime[a[i].t + a[j].t]) || (a[i].t + a[j].t > 1000000 && checkPrime(a[i].t + a[j].t))) hasEdge[i][j] = hasEdge[j][i] = 1; } bool check(int x) { memset(head, 0, sizeof head); tot = 1; int mx = 0, ans = 0; rep(i, 1, n) if (a[i].l <= x) { if (a[i].t ^ 1) { if (a[i].t & 1) add(S, i, a[i].p); else add(i, T, a[i].p); ans += a[i].p; } else if (a[i].p > a[mx].p) mx = i; } if (mx) add(S, mx, a[mx].p), ans += a[mx].p; rep(i, 1, n) rep(j, 1, n) if (a[i].l <= x && a[j].l <= x && a[i].t % 2 == 1 && a[j].t % 2 == 0 && hasEdge[i][j]) add(i, j, INF); while (bfs()) ans -= dfs(S, INF); return ans >= K; } #define mid (l + r >> 1) int main() { int l = 1, r = 0, ans = 0; cin >> n >> K; rep(i, 1, n) a[i].in(), r = max(r, a[i].l); T = n + 1; makePrime(); init(); while (l <= r) if (check(mid)) ans = mid, r = mid - 1; else l = mid + 1; cout << (ans ? ans : -1); return 0; }