[CF808F] Card Game(最大流,最大点权独立集,二分)

题目链接:http://codeforces.com/contest/808/problem/F

题意:给出n个三元组,和一个整数k。包括三个属性p c l。要求选出的几个三元组满足∑pi>=k,并且还要满足:

选出的任意两个ci+cj不是素数。

额外有一个变量lv,不能选择li > lv的三元组。

求这个lv的最小值是多少,可以满足上述条件。

 

转换成二分图来做,考虑奇数和奇数的和为偶数,必然不是素数;偶数和偶数的和为偶数,必然不是素数。所以我们可以按照奇偶来区分点,这样就不用拆点了。

相当于是以两点权值和为素数的条件下的最大点权独立集,希望找到这么一个点集,使他们没有边相连(和不是素数)。

 

结果就WA了,为什么呢?

看了tutorial才知道,假如有多个1存在,会破坏这个二分图的性质。因为>=2的时候,1被分在奇数侧,而实际上可以选2个1构成一个素数,显然不符合条件。

所以最多只能有一个1,有的话,贪心地选p最大的那个就行。

根据定理,最大点券独立集=点权和-最小割。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 const int maxm = 500000;
  5 const int maxn = 2010;
  6 const int inf = 0x7f7f7f7f;
  7 
  8 typedef struct Edge { int u, v, w, next; }Edge;
  9 bool isprime[maxm];
 10 int prime[maxm];
 11 int pcnt;
 12 int cnt, dhead[maxn];
 13 int cur[maxn], dd[maxn];
 14 Edge dedge[maxm];
 15 int S, T, N;
 16 
 17 void getPrime() {
 18     memset(isprime, true, sizeof(isprime));
 19     memset(prime, 0, sizeof(prime));
 20     pcnt = 0;
 21     prime[0] = prime[1] = 0;
 22     for(int i = 2; i <= maxm; i++) {
 23         if(isprime[i]) prime[++pcnt] = i;
 24         for(int j = 1; j <= pcnt; j++) {
 25             if(i * prime[j] > maxm) break;
 26             isprime[i*prime[j]] = 0;
 27             if(i % prime[j] == 0) break;
 28         }
 29     }
 30 }
 31 
 32 void init() {
 33     memset(dhead, -1, sizeof(dhead));
 34     for(int i = 0; i < maxn; i++) dedge[i].next = -1;
 35     S = T = N = 0; cnt = 0;
 36 }
 37 
 38 void adde(int u, int v, int w, int c1=0) {
 39     dedge[cnt].u = u; dedge[cnt].v = v; dedge[cnt].w = w; 
 40     dedge[cnt].next = dhead[u]; dhead[u] = cnt++;
 41     dedge[cnt].u = v; dedge[cnt].v = u; dedge[cnt].w = c1; 
 42     dedge[cnt].next = dhead[v]; dhead[v] = cnt++;
 43 }
 44 
 45 bool bfs(int s, int t, int n) {
 46     queue<int> q;
 47     for(int i = 0; i < n; i++) dd[i] = inf;
 48     dd[s] = 0;
 49     q.push(s);
 50     while(!q.empty()) {
 51         int u = q.front(); q.pop();
 52         for(int i = dhead[u]; ~i; i = dedge[i].next) {
 53             if(dd[dedge[i].v] > dd[u] + 1 && dedge[i].w > 0) {
 54                 dd[dedge[i].v] = dd[u] + 1;
 55                 if(dedge[i].v == t) return 1;
 56                 q.push(dedge[i].v);
 57             }
 58         }
 59     }
 60     return 0;
 61 }
 62 
 63 int dinic(int s, int t, int n) {
 64     int st[maxn], top;
 65     int u;
 66     int flow = 0;
 67     while(bfs(s, t, n)) {
 68         for(int i = 0; i < n; i++) cur[i] = dhead[i];
 69         u = s; top = 0;
 70         while(cur[s] != -1) {
 71             if(u == t) {
 72                 int tp = inf;
 73                 for(int i = top - 1; i >= 0; i--) {
 74                     tp = min(tp, dedge[st[i]].w);
 75                 }
 76                 flow += tp;
 77                 for(int i = top - 1; i >= 0; i--) {
 78                     dedge[st[i]].w -= tp;
 79                     dedge[st[i] ^ 1].w += tp;
 80                     if(dedge[st[i]].w == 0) top = i;
 81                 }
 82                 u = dedge[st[top]].u;
 83             }
 84             else if(cur[u] != -1 && dedge[cur[u]].w > 0 && dd[u] + 1 == dd[dedge[cur[u]].v]) {
 85                 st[top++] = cur[u];
 86                 u = dedge[cur[u]].v;
 87             }
 88             else {
 89                 while(u != s && cur[u] == -1) {
 90                     u = dedge[st[--top]].u;
 91                 }
 92                 cur[u] = dedge[cur[u]].next;
 93             }
 94         }
 95     }
 96     return flow;
 97 }
 98 
 99 int n, k;
100 int p[maxn], c[maxn], l[maxn];
101 
102 int gao(int lv) {
103     init();
104     S = 0; T = n + 1; N = T + 1;
105     int tot = 0;
106     int id, mx = -inf;
107     for(int i = 1; i <= n; i++) {
108         if(l[i] > lv) continue;
109         if(c[i] == 1 && p[i] > p[id]) id = i;
110     }
111     for(int i = 1; i <= n; i++) {
112         if(l[i] > lv) continue;
113         if(c[i] == 1 && i != id) continue;
114         tot += p[i];
115         if(c[i] & 1) adde(S, i, p[i]);
116         else adde(i, T, p[i]);
117     }
118     for(int i = 1; i <= n; i++) {
119         for(int j = 1; j <= n; j++) {
120             if(isprime[c[i]+c[j]]) {
121                 if(c[i] & 1) adde(i, j, inf);
122                 else adde(j, i, inf);
123             }
124         }
125     }
126     return tot - dinic(S, T, N) >= k;
127 }
128 
129 int main() {
130     // freopen("in", "r", stdin);
131     getPrime();
132     while(~scanf("%d%d",&n,&k)) {
133         int mx = -1;
134         for(int i = 1; i <= n; i++) {
135             scanf("%d%d%d",&p[i],&c[i],&l[i]);
136             mx = max(mx, l[i]);
137         }
138         int lo = 1, hi = mx;
139         int ret = -1;
140         while(lo <= hi) {
141             int mid = (lo + hi) >> 1;
142             if(gao(mid)) {
143                 ret = mid;
144                 hi = mid - 1;
145             }
146             else lo = mid + 1;
147         }
148         printf("%d\n", ret);
149     }
150     return 0;
151 }

 

转载于:https://www.cnblogs.com/kirai/p/6944515.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值