zoukankan      html  css  js  c++  java
  • 牛客多校1——1or2

    题意

      给定一张n点m边的图,以及每个点的理想出入度,求是否能够通过选择某些边来使得新图上每个点的出入度为理想出入度

    做法

      由于d[i]的范围为1~2,说明新图中的连通分量要么是链,要么是环。对于此类最大匹配问题,可以用网络流来解决,但是由于可能存在奇环,这将导致找增广路时,传统的dinic算法时间复杂度陡增。

      如何处理带奇环的图最大匹配问题,可以使用带花树算法来做,也就是一般图最大图匹配问题。

      当两个点的出入度都等于1时,直接把两个点连起来

      当两个相连点的出入度都等于2时,把点点之间的连接,转化成每个点拆成出入两个点,把边看做是一个单独的点,拆成出入两个点,将每个点的出入点与夹边的出入点相连接,并且将夹边的入点连向出点

      当相连的两个点出入度有一个为2一个为1时,不仅将两个点同时相连,还需要将出入度为1的点像出入度为2的点的出点连接。

      如此我们可以构造出一个一般图

      然后利用带花树算法跑此图的最大匹配

      注意一个特判,由于链有两端,所以最后得到的点的总度数之和不可以为奇数。

      最后判断一下跑出来的最大匹配是否等于总度数之和即可

    CODE

      1 #include <bits/stdc++.h>
      2 #define dbg(x) cout << #x << "=" << x << endl
      3 #define eps 1e-8
      4 #define pi acos(-1.0)
      5 
      6 using namespace std;
      7 typedef long long LL;
      8 
      9 const int inf = 0x3f3f3f3f;
     10 
     11 template<class T>inline void read(T &res)
     12 {
     13     char c;T flag=1;
     14     while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
     15     while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
     16 }
     17 
     18 namespace _buff {
     19     const size_t BUFF = 1 << 19;
     20     char ibuf[BUFF], *ib = ibuf, *ie = ibuf;
     21     char getc() {
     22         if (ib == ie) {
     23             ib = ibuf;
     24             ie = ibuf + fread(ibuf, 1, BUFF, stdin);
     25         }
     26         return ib == ie ? -1 : *ib++;
     27     }
     28 }
     29 
     30 int qread() {
     31     using namespace _buff;
     32     int ret = 0;
     33     bool pos = true;
     34     char c = getc();
     35     for (; (c < '0' || c > '9') && c != '-'; c = getc()) {
     36         assert(~c);
     37     }
     38     if (c == '-') {
     39         pos = false;
     40         c = getc();
     41     }
     42     for (; c >= '0' && c <= '9'; c = getc()) {
     43         ret = (ret << 3) + (ret << 1) + (c ^ 48);
     44     }
     45     return pos ? ret : -ret;
     46 }
     47 
     48 const int maxn = 1e6 + 7;
     49 
     50 int n;
     51 int m, m1;
     52 int ti;
     53 
     54 //一般图最大匹配模板
     55 
     56 int fs[maxn], nt[maxn <<1];
     57 int dt[maxn << 1], pre[maxn], match[maxn];
     58 int f[maxn], bz[maxn], bp[maxn], d[maxn];
     59 
     60 void link(int x,int y)
     61 {
     62     nt[++m1]=fs[x];
     63     dt[fs[x]=m1]=y;
     64 }
     65 int getf(int k)
     66 {
     67     return (f[k]==k)?k:f[k]=getf(f[k]);
     68 }
     69 int lca(int x,int y)//整个lca实现比较巧妙,由于是BFS,那么这两个点在当前奇环上的深度一定相等,交替暴力寻找lca即可。
     70 {
     71     ti++;x=getf(x),y=getf(y);
     72     while(bp[x]!=ti)
     73     {
     74         bp[x]=ti;//此处仅仅是一个标记,无其他作用
     75         x=getf(pre[match[x]]);
     76         if(y) swap(x,y);
     77     }
     78     return x;
     79 }
     80 void make(int x,int y,int w)//缩环(开花)过程
     81 {
     82     while(getf(x)!=w)
     83     {
     84         pre[x]=y,y=match[x];//x是原本的黑点,y是原本的白点,将原本的pre边变成双向。
     85         if(bz[y]==2) bz[y]=1,d[++d[0]]=y;//若y还是白点则染黑
     86         if(getf(x)==x) f[x]=w;
     87         if(getf(y)==y) f[y]=w;
     88         x=pre[y];
     89     }
     90 }
     91 bool find(int st)//主过程
     92 {
     93     for ( int i = 1; i <= n; ++i ) {
     94         f[i]=i,pre[i] = bz[i] = 0;
     95     }
     96     d[d[0] = 1] = st,bz[st] = 1;
     97     int l = 0;
     98     while(l<d[0])
     99     {
    100         int k = d[++l];
    101         for(int i = fs[k];i;i = nt[i])
    102         {
    103             int p = dt[i];
    104             if(getf(p) == getf(k)||bz[p] == 2) continue;//如果找到一个已经缩过的奇环或者偶环则跳过
    105             if(!bz[p])
    106             {
    107                 bz[p] = 2,pre[p] = k;
    108                 if(!match[p])//找到增广路
    109                 {
    110                     for(int x = p,y;x;x = y) y = match[pre[x]],match[x] = pre[x],match[pre[x]] = x;//返回修改匹配
    111                     return 1;
    112                 }
    113                 bz[match[p]] = 1,d[++d[0]] = match[p];//否则将其匹配点加入队列
    114             }
    115             else
    116             {
    117                 int w = lca(k,p);
    118                 make(k,p,w);
    119                 make(p,k,w);//以上分别修改k到lca的路径以及p到lca的路径(环的两半)
    120             }
    121         }
    122     }
    123     return 0;
    124 }
    125 //一般图最大匹配模板
    126 
    127 int idx[maxn],ide[maxn],deg[maxn];
    128 
    129 int main() {
    130     while(cin >> n >> m) {
    131         int nn = n, sum = 0, ss = 0;
    132    
    133         int ok = 1, t = 0;
    134         for ( int i = 1; i <= n; ++i ) {
    135             read(deg[i]);
    136             sum += deg[i];
    137             idx[i] = ++ss;
    138             if(deg[i] == 2)
    139                 ++ss;
    140         }
    141         for ( int i = 1; i <= m; ++i ) {
    142             int u,v;
    143             read(u); read(v);
    144             if(deg[u] == 2 && deg[v] == 2) {
    145                 ide[i] = ++ss;
    146                 ss++;
    147             }
    148     
    149             if(deg[u] == 1&& deg[v] == 1)
    150                 link(idx[u],idx[v]),link(idx[v],idx[u]);
    151             else if(deg[u] == 2 && deg[v] == 2) {
    152                 link(idx[u],ide[i]);link(ide[i],idx[u]);
    153                 link(idx[u]+1,ide[i]);link(ide[i],idx[u]+1);
    154     
    155                 link(ide[i],ide[i]+1);link(ide[i]+1,ide[i]);
    156     
    157                 link(idx[v],ide[i]+1);link(ide[i]+1,idx[v]);
    158                 link(idx[v]+1,ide[i]+1);link(ide[i]+1,idx[v]+1);
    159             }
    160             else {
    161                 if(deg[u] == 2) {
    162                     swap(u,v);
    163                 }
    164                 link(idx[v],idx[u]);link(idx[u],idx[v]);
    165                 link(idx[v]+1,idx[u]);link(idx[u],idx[v]+1);
    166             }
    167         }
    168         if(sum & 1)
    169             ok = 0;
    170         n = ss;
    171     
    172         for ( int i = 1; i <= nn; ++i ) {
    173             if(!match[idx[i]]) {
    174                 find(idx[i]);
    175             }
    176             if(deg[i] > 1 && !match[idx[i]+1]) {
    177                 find(idx[i]+1);
    178             }
    179         }
    180     
    181         for ( int i = 1; i <= nn; ++i ) {
    182             if(match[idx[i]])
    183                 t++;
    184             if(deg[i] > 1 && match[idx[i]+1])
    185                 t++;
    186         }
    187         
    188         if(t != sum || ok == 0) {
    189             puts("No");
    190         }
    191         else {
    192             puts("Yes");
    193         }
    194         m1 = 0;
    195         for ( int i = 0; i <= n; ++i ) {
    196             fs[i] = nt[i] = dt[i] = pre[i] = match[i] = f[i] = bz[i] = bp[i] = 0;
    197         }
    198         ti = 0;
    199     }
    200     return 0;
    201 }
    202  
  • 相关阅读:
    php分享三十:php版本选择
    php分享二十九:命名空间
    高性能mysql读书笔记(一):Schema与数据类型优化
    php分享二十八:mysql运行中的问题排查
    php分享二十七:批量插入mysql
    php分享二十六:读写日志
    Python | 一行命令生成动态二维码
    Python-获取法定节假日
    GoLang-字符串
    基础知识
  • 原文地址:https://www.cnblogs.com/orangeko/p/13338473.html
Copyright © 2011-2022 走看看