用最大流是个假算法(网络流题解),虽然牛客没有重测,我还是来补一发带花树正解
首先建图是这样的:
每个条边拆成两个点x,y,x和y连边
这条边连的两个点u,v,u拆成d[u]个点,分别和x连边,v拆成d[v]个点,分别和y连边
然后如果是完全匹配,就输出yes
解释一下为什么完全匹配就能yes
如果是完全匹配,每个点都被匹配上了,这样就达到了题目要求的每个点必须连di条边的要求,但是,这是否能保证一条边上两个点,如果一点和该边相连,另一点也同时相连的效果呢?
当然是可以的,比如对于建图部分所说的点v,他如果匹配到了x,那么要达到完全匹配,y已经不能和x匹配,那必和v(或它拆出来的点)匹配,这样就达到了边上每个点必定同时匹配这条边的效果
链接:https://ac.nowcoder.com/acm/contest/5666/I
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
Bobo has a graph with n vertices and m edges where the i-th edge is between the vertices aia_iai and bib_ibi. Find out whether is possible for him to choose some of the edges such that the i-th vertex is incident with exactly did_idi edges.
输入描述:
The input consists of several test cases terminated by end-of-file. The first line of each test case contains two integers n and m. The second line contains n integers d1,d2,…,dnd_1, d_2, \dots, d_nd1,d2,…,dn. The i-th of the following m lines contains two integers aia_iai and bib_ibi. * 1≤n≤501 \leq n \leq 501≤n≤50 * 1≤m≤1001 \leq m \leq 1001≤m≤100
* 1≤di≤21 \leq d_i \leq 21≤di≤2
* 1≤ai,bi≤n1 \leq a_i, b_i \leq n1≤ai,bi≤n
* ai≠bia_i \neq b_iai=bi
* {ai,bi}≠{aj,bj}\{a_i, b_i\} \neq \{a_j, b_j\}{ai,bi}={aj,bj} for i≠ji \neq ji=j
* The number of tests does not exceed 100.
输出描述:
For each test case, print "`Yes`" without quotes if it is possible. Otherwise, print "`No`" without quotes.
示例1
输入
复制2 1 1 1 1 2 2 1 2 2 1 2 3 2 1 1 2 1 3 2 3
2 1 1 1 1 2 2 1 2 2 1 2 3 2 1 1 2 1 3 2 3
输出
复制Yes No Yes
Yes No Yes
#include<bits/stdc++.h>
#pragma GCC optimize(3)
using namespace std;
#include<bits/stdc++.h>
using namespace std;
#define I inline int
#define V inline void
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define REP(u) for(int i=h[u],v;v=e[i].t,i;i=e[i].n)
const int N=1e3+1,M=1e5+1;
queue<int>q;
int n,m,tot,qwq,ans;
int h[N],lk[N],tag[N],fa[N],pre[N],dfn[N];
struct edge{int t,n;}e[M];
V link(int x,int y){lk[x]=y,lk[y]=x;}
V add_edge(int x,int y){
if(!lk[x]&&!lk[y])link(x,y),ans++;
e[++tot]=(edge){y,h[x]},h[x]=tot;
e[++tot]=(edge){x,h[y]},h[y]=tot;
}
V rev(int x){if(x)rev(x[pre][lk]),link(x,pre[x]);}
I find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
I lca(int x,int y){
for(qwq++;;x=x[lk][pre],swap(x,y))
if(dfn[x=find(x)]==qwq)return x;
else if(x)dfn[x]=qwq;
}
V shrink(int x,int y,int p){
for(;find(x)!=p;x=pre[y]){
pre[x]=y,y=lk[x],fa[x]=fa[y]=p;
if(tag[y]==2)tag[y]=1,q.push(y);
}
}
I blossom(int u){
FOR(i,1,n)tag[i]=pre[i]=0,fa[i]=i;
tag[u]=1,q=queue<int>(),q.push(u);
for(int p;!q.empty();q.pop())REP(u=q.front())
if(tag[v]==1)
p=lca(u,v),shrink(u,v,p),shrink(v,u,p);
else if(!tag[v]){
pre[v]=u,tag[v]=2;
if(!lk[v])return rev(v),1;
else tag[lk[v]]=1,q.push(lk[v]);
}
return 0;
}
vector<int>a[N];
int d[N];
void init()
{
for(int i=1;i<=N-10;i++) a[i].clear();
ans=tot=qwq=0;
memset(h,0,sizeof(h));
memset(fa,0,sizeof(fa));
memset(pre,0,sizeof(pre));
memset(dfn,0,sizeof(dfn));
memset(lk,0,sizeof(lk));
memset(tag,0,sizeof(tag));
memset(d,0,sizeof(d));
}
int main(){
while(scanf("%d %d",&n,&m)!=EOF)
{
init();
for(int x,y;m--;add_edge(x,y))scanf("%d%d",&x,&y);
FOR(i,1,n)ans+=!lk[i]&&blossom(i);
cout<<ans<<'\n';
FOR(i,1,n)cout<<lk[i]<<' ';
}
}