幻想鄉的符卡

###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;
}
相關文章
相關標籤/搜索