zoukankan      html  css  js  c++  java
  • [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
    ", ret);
    149     }
    150     return 0;
    151 }
  • 相关阅读:
    A % B Problem
    封锁阳光大学
    数楼梯
    海滩防御
    修复公路
    四子连棋
    口袋的天空
    兔子数
    逆序对&求逆序对
    【模板】单源最短路径*
  • 原文地址:https://www.cnblogs.com/kirai/p/6944515.html
Copyright © 2011-2022 走看看