###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; }