zoukankan      html  css  js  c++  java
  • [CQOI2017]老C的方块 网络流

    ~~~题面~~~

    题解:

    做这题做了好久,,,换了4种建图QAQ

    首先我们观察弃疗的形状,可以发现有一个特点,那就是都以一个固定不变的特殊边为中心的,如果我们将特殊边两边的方块分别称为s块和t块,

    那么我们可以观察到,s块和t块永远是在中心位置,而其他两块则是紧邻s块和t块,一边一个。

    所以我们要考虑将这个图像用一根线串起来,这样跑最小割才能割最小的边,

    那么如何做到一条边割几个图形呢?

    首先我们观察到一个非st方块本来就可以属于多个图形,因此也会有多条连边,因此我们只需要对每个方块拆点,限制其只能割一次就可以了。

    一开始我选择的连图方式是:

    以特殊边左边为s块,右边为t块,s连向s块,t块连向t(s块和t块名字来源),s块连向四周的非t块,t块四周的非s块连向它,s块四周的非t块连向t块四周的非s块。

    但这样为什么这样不行呢?

    我们可以观察到这样一种情况,

    可以发现,右边的小圆圈同时处在s块和t块周围,那么由于扮演了两个角色,这就有可能导致右边的s块的流量流到左边的t块里,于是就出现了不合法情况。

    …………

    遂交换偶行的s块和t块。

    可以发现,右边的s块和左边的t块构成了一个不合法图形,那么这是为什么?

    显然是上面反过来的t块和s块在勾桥搭线。。。。

    那怎么办?

    我们再观察一下图形。既然所有图形都是以s块和t块为中心,那么这两块显然是更加稳定的,因此我们不再用其他块作为中转块,而是采用以s块和t块为中转。

    即:

    s ---> s块周围的块 ----> s块 ---> t块 ---> t块周围的块

    经验证,可以满足要求。

    我是强行暴力人工分类讨论建图的。。。因此代码很长,建图就有60行。。。。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define inf 2139062143
      5 #define getchar() *o++
      6 #define AC 200100
      7 #define ac 1700000
      8 
      9 char READ[7001000], *o = READ;
     10 int n, x, ans, s, t, addflow, X, Y;
     11 int last[AC], good[AC], have[AC], c[AC], power[AC];
     12 int q[AC], head, tail;
     13 int date[ac], Next[ac], haveflow[ac], Head[AC], tot = 1;
     14 
     15 struct point{
     16     int x, y;
     17 }p[AC];
     18 
     19 bool operator < (point a, point b)
     20 {
     21     if(a.x != b.x) return a.x < b.x;
     22     else return a.y < b.y;
     23 }
     24 
     25 map<point, int> m;
     26  
     27 inline int read()
     28 {
     29     int x=0;char c=getchar();
     30     while(c > '9' || c < '0') c = getchar();
     31     while(c >= '0' && c <= '9') x=x*10+c-'0',c=getchar();
     32     return x;
     33 }
     34 
     35 inline void add(int f, int w, int S)
     36 {
     37     date[++tot] = w, Next[tot] = Head[f], haveflow[tot] = S, Head[f] = tot;
     38     date[++tot] = f, Next[tot] = Head[w],/* haveflow[tot] = 0,*/ Head[w] = tot;
     39 //    printf("%d ---> %d %d
    ", f, w, S);
     40 }
     41 
     42 void pre()
     43 {
     44     X = read(), Y = read(), n = read();
     45     s = n * 2 + 1, t = s + 1;
     46     for(R i=1;i<=n;i++)
     47     {
     48         p[i].y = read(), p[i].x = read(), power[i] = read();//注意行列是反的!
     49         m[p[i]] = i;//编号
     50     }
     51 }
     52 
     53 void build()//暴力枚举加边
     54 {
     55     int x, y;
     56     for(R i=1;i<=n;i++)//枚举方块,只连出边
     57     {
     58         add(i, n + i, power[i]);//拆点连边
     59         x = p[i].x, y = p[i].y;
     60         if(x % 2)//按奇偶行分类 
     61         {
     62             if(y % 2)//按奇偶列分类(可能为s or t右)
     63             {//因为2 = 2 * 1, 6 = 2 * 3 .... 所以有余数就说明是s
     64                 if(((y + 1) / 2) % 2)//有余数所以是s    
     65                 {//跟旁边的t连上
     66                     if(m[(point){x, y + 1}]) add(i + n, m[(point){x, y + 1}], inf);
     67                 }
     68                 else add(i + n, t, inf);//不然就要连向t了    
     69             }
     70             else//不然就是s左的 or t
     71             {
     72                 if((y / 2) % 2) 
     73                 {
     74                     if(m[(point){x, y + 1}]) add(i + n, m[(point){x, y + 1}], inf);//如果有余数说明是t
     75                     if(m[(point){x + 1, y}]) add(i + n, m[(point){x + 1, y}], inf);
     76                     if(m[(point){x - 1, y}]) add(i + n, m[(point){x - 1, y}], inf);
     77                 }
     78                 else//不然属于s周围的
     79                 {
     80                     if(m[(point){x, y + 1}]) add(i + n, m[(point){x, y + 1}], inf);
     81                     if(m[(point){x + 1, y}]) add(i + n, m[(point){x + 1, y}], inf);
     82                     if(m[(point){x - 1, y}]) add(i + n, m[(point){x - 1, y}], inf);
     83                     add(s, i, inf);//注意s周围的要连s
     84                 }
     85             }
     86         }
     87         else //偶行
     88         {
     89             if(y % 2)//奇列,t or s右
     90             {
     91                 if(((y + 1) / 2) % 2)//如果有余数说明是s右
     92                 {
     93                     if(m[(point){x, y - 1}]) add(i + n, m[(point){x, y - 1}], inf);
     94                     if(m[(point){x + 1, y}]) add(i + n, m[(point){x + 1, y}], inf);
     95                     if(m[(point){x - 1, y}]) add(i + n, m[(point){x - 1, y}], inf);        
     96                     add(s, i, inf);        
     97                 }
     98                 else
     99                 {
    100                     if(m[(point){x, y - 1}]) add(i + n, m[(point){x, y - 1}], inf);//如果有余数说明是t
    101                     if(m[(point){x + 1, y}]) add(i + n, m[(point){x + 1, y}], inf);
    102                     if(m[(point){x - 1, y}]) add(i + n, m[(point){x - 1, y}], inf);                
    103                 }
    104             }
    105             else//t左 or s
    106             {
    107                 if((y / 2) % 2)    add(i + n, t, inf);//如果有余数的话,说明是t左
    108                 else//不然就是s
    109                     if(m[(point){x, y - 1}]) add(i + n, m[(point){x, y - 1}], inf);
    110             }//注意加了n才是出发点
    111         }
    112     }
    113 }
    114 
    115 void bfs()
    116 {
    117     int x, now;
    118     c[t] = 1, x = t, q[++tail] = t;
    119     while(head < tail)
    120     {
    121         x = q[++head];
    122         for(R i=Head[x]; i; i=Next[i])
    123         {
    124             now = date[i];
    125             if(haveflow[i^1] && !c[now])
    126             {
    127                 ++have[c[now] = c[x] + 1];
    128                 q[++tail] = now;
    129             }
    130         }
    131     } 
    132     memcpy(good, Head, sizeof(Head));
    133 }
    134 
    135 inline void aru()
    136 {
    137     while(x != s)
    138     {
    139         haveflow[last[x]] -= addflow;
    140         haveflow[last[x] ^ 1] += addflow;
    141         x = date[last[x] ^ 1];
    142     }
    143     ans += addflow;
    144 }
    145 
    146 void isap()
    147 {
    148     int now; bool done;
    149     addflow = inf, x = s;
    150     while(c[s] != 200051)
    151     {
    152         if(x == t) aru(), addflow = inf;
    153         done = false;
    154         for(R i =good[x]; i ;i =Next[i])
    155         {
    156             now = date[i];
    157             if(haveflow[i] && c[now] == c[x] -1)
    158             {
    159                 addflow = min(addflow, haveflow[i]);
    160                 last[now] = i;
    161                 good[x] = i;
    162                 done = true;
    163                 x = now;
    164                 break;
    165             }
    166         }
    167         if(!done)
    168         {
    169             int go = 200050;
    170             for(R i=Head[x]; i ;i=Next[i])
    171             {
    172                 now = date[i]; 
    173                 if(haveflow[i] && c[now]) go = min(go, c[now]);
    174             }
    175             good[x] = Head[x];
    176             if(!(--have[c[x]])) break;
    177             ++have[c[x] = go + 1];
    178             if(x != s) x = date[last[x] ^ 1];
    179         }
    180     }
    181     printf("%d
    ", ans);
    182 }
    183 
    184 int main()
    185 {
    186 //    freopen("in.in","r",stdin);
    187     fread(READ, 1, 7000000, stdin);
    188     pre();
    189     build();
    190     bfs();
    191     isap();
    192 //    fclose(stdin);
    193     return 0;
    194 }
  • 相关阅读:
    线程池execute执行顺序
    三个线程交替打印1到100
    mysql优化
    最大回文子串
    AOP实现日志收集和记录
    LoadingCache缓存使用(LoadingCache)
    springboot项目在idea中实现热部署
    idea破解
    linux常用命令
    Oracle的分条件计数COUNT(我的条件),由浅入深
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9216233.html
Copyright © 2011-2022 走看看