幻想乡的符卡

Description

灵梦和魔理沙即将展开弹幕大战,为了提高实力,灵梦打算突击学习一些符卡。每张符卡有三个属性:火力、时长和等级。求胜心切的灵梦希望在一次战斗中,使用的符卡的火力值总和不小于 \(k\) 。然而,由于一些特殊的原因,如果两张符卡的时长之和是一个质数,那么它们便不能在一次战斗中同时使用。此外,如果符卡的等级超过了灵梦的等级,她也无法学习这张符卡。由于提升等级是一件很困难的事情,灵梦想要知道,为了达到目标,自己所需的最低等级是多少。

Input

第一行两个整数 \(n, k\) ,表示可供学习的符卡数量,以及灵梦希望达到的火力总和。

接下来 \(n\) 行,每行三个整数 \(p_i, t_i, l_i\) 分别表示一张符卡的火力、时长和等级。

Output

一行一个整数,表示灵梦所需的最低等级。如果找不到满足要求的一组符卡,输出 \(−1\)

Sample Input

5 8
5 5 1
1 5 4
4 6 3
1 12 4
3 12 1

Sample Output

4

Solution

最小割。

首先二分一下等级,接下来所有不满足等级条件的卡全部忽略。

然后建图。若 \(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\) 的除外)。

为什么这样建图呢?首先中间的 \(INF\) 边是假设不合法的卡都能取,然后跑最小割来舍弃。而且我们知道奇数+奇数、偶数+偶数都等于偶数,不可能是质数,所以左边内部和右边内部不可能有边,所以这样建图。

但是!\(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;
}
posted @ 2018-02-08 23:18  aziint  阅读(557)  评论(0编辑  收藏  举报
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.