7.12图论练习赛 T2 幻想乡的符卡 (网络流最小割)

来源:互联网 发布:淘宝购物车logo 编辑:程序博客网 时间:2024/05/02 01:06

题目

这里写图片描述

题解

这题主要是考察建边。限制条件有三:等级,火力,时长。
解决等级和火力的方法是二分答案,可以二分等级,将不满足等级的符卡直接跳过,建边check火力大小的值。
解决时长有两个部分:普通的通过判断和是否位素数(可以预处理,快很多),如果为素数就在两个数间建一条∞的边。第二个部分是特判1。由于两个1相加为2,也是素数,所以应该特判在所有满足等级小于等于mid的时长为1的符卡中,只取一张火力值最大的符卡。将所有加入建边的时长分为奇数和偶数,奇数与源点连边,偶数与汇点连边,边权即这张符卡的火力值。(除1+1以外,奇数加奇数、偶数加偶数必为素(偶)数,所以分成两部分)
跑网络流的思路是假设所有的卡都能取(所以需要记录所有满足的等级的符卡的火力值之和,但所有时长为1的符卡中只取一张,只加一次),在相连的边中跑最小割来表示需要割掉的卡。最后用sum - Maxflow的值就是在当前等级下能获得的最大火力值。

代码

#include <cstdio>#include <cstring>#include <algorithm>#define oo 0x3fffffffusing namespace std;struct Edge {    int v, next, f;} edge[1000005];int s, tt, dis[1000005], queue[1000005], head[1000005], num = 1;int n, k, cnt = 0, p[500], t[500], l[500], used[500], prime[1000005];bool vis[1000005], isnot[10000005], sure[500][500];void add(int u, int v, int f) {    num ++;    edge[num].v = v;    edge[num].f = f;    edge[num].next = head[u];    head[u] = num;    num ++;    edge[num].v = u;    edge[num].f = 0;    edge[num].next = head[v];    head[v] = num;}void zero() {    memset(vis, 0, sizeof(vis));    memset(dis, 0, sizeof(dis));    memset(queue, 0, sizeof(queue));}bool bfs() {    int h = 0, tail = 1;    vis[s] = 1; dis[s] = 0; queue[1] = s;    while(h < tail) {        int u = queue[++ h];        for(int i = head[u]; i; i = edge[i].next) {            int v = edge[i].v;            if(! vis[v] && edge[i].f) {                vis[v] = 1;                queue[++ tail] = v;                dis[v] = dis[u] + 1;            }        }    }    if(vis[tt]) return true;    return false;}int dfs(int u, int delta) {    if(u == tt || ! delta) return delta;    int ans = 0;    for(int i = head[u]; i && delta; i = edge[i].next) {        int v = edge[i].v;        if(dis[v] == dis[u] + 1 && edge[i].f) {            int dd = dfs(v, min(delta, edge[i].f));            edge[i].f -= dd;            edge[i ^ 1].f += dd;            delta -= dd;            ans += dd;        }    }    if(! ans) dis[u] = -1;    return ans;}int Maxflow() {    int ans = 0;    while(1) {        zero();        if(! bfs()) break;        ans += dfs(s, oo);    }    return ans;}void clear() {    num = 1;    memset(used, 0, sizeof(used));    memset(head, 0, sizeof(head));}void prm() {    memset(isnot, 0, sizeof(isnot));    isnot[1] = true;    for(register int i = 2; i <= 1000000; i ++) {        if(! isnot[i])            prime[++ cnt] = i;        for(int j = 1; j <= cnt; j ++) {            int u = i * prime[j];            if(u > 1000000) break;            isnot[u] = true;            if(i % prime[j] == 0) break;        }    }}bool check(int x) {    for(register int i = 1; i <= cnt; i ++){        if(prime[i] * prime[i] > x) break;        if(x % prime[i] == 0) return false;    }    return true;}void init() {    for(int i = 1; i <= n; i ++)        for(int j = 1; j <= n; j ++) {            if(t[i] + t[j] <= 1000000 && ! isnot[t[i] + t[j]])                sure[i][j] = 1;            else if(t[i] + t[j] >= 1000000 && check(t[i] + t[j]))                sure[i][j] = 1;        }}int main() {    freopen("card.in", "r", stdin);    freopen("card.out", "w", stdout);    scanf("%d %d", &n, &k);    for(int i = 1; i <= n; i ++)         scanf("%d %d %d", &p[i], &t[i], &l[i]);    s = 0, tt = n + 1;    prm();    init();    int L = 0, R = 1000000002, best, b, sum;    while(L < R) {        int mid = (L + R) >> 1;        clear();        best = 0, b = 0, sum = 0;        for(int i = 1; i <= n; i ++) {            if(l[i] > mid) continue;            if(t[i] == 1) {                if(best < p[i]) {                    best = p[i];                    b = i;                }                continue;            }            sum += p[i];            if(t[i] % 2 == 0) continue;            for(int j = 1; j <= n; j ++) {                if(l[j] > mid) continue;                if(t[j] % 2 == 1) continue;                if(sure[i][j]) {                    add(i, j, oo);                    if(! used[i]) {                        add(s, i, p[i]);                        used[i] = 1;                    }                    if(! used[j]) {                        add(j, tt, p[j]);                        used[j] = 1;                    }                }            }        }        if(b != 0) {            for(int i = 1; i <= n; i ++) {                if(l[i] > mid) continue;                if(t[i] == 1) continue;                if(sure[i][b]) {                    add(b, i, oo);                    if(! used[b]) {                        add(s, b, best);                        used[b] = 1;                    }                    if(! used[i]) {                        add(i, tt, p[i]);                        used[i] = 1;                    }                }            }        }        sum += p[b];        if(sum - Maxflow() < k) L = mid + 1;        else R = mid;    }    if(R != 1000000002) printf("%d", R);    else printf("-1");    return 0;}