题意
给定一张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