• 幻想乡的符卡


    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;
    }
    
  • 相关阅读:
    WEB前端开发工具的初识
    Linux常见问题的处理方法(长期更新)
    eclipse+tomcat开发web项目
    Android适配--百分比的适配
    TCP通信丢包原因总结
    android 实现类似微信缓存和即时更新好友头像
    是否应该开启WebView硬件加速?
    Android通过浏览器打开app页面并且传递值
    设计模式(五)责任链模式
    Android 最新 Support V4 包大拆分有用吗?
  • 原文地址:https://www.cnblogs.com/aziint/p/8433647.html
Copyright © 2020-2023  润新知