链接:https://ac.nowcoder.com/acm/contest/5666/I
Bobo has a graph with n vertices and m edges where the i-th edge is between the vertices ai and bi. Find out whether is possible for him to choose some of the edges such that the i-th vertex is incident with exactly d[i] edges.
题目大意:给了一个无向图,问能否选择一些边,使得每个点的度数都为d[i];
思路:拆点拆边完 就是跑一般图的最大匹配了,看能否匹配成功。
可能你会在图中看到两个3 实际上式将3 这个点按照度d 3 = 2 拆分成3和3’
4、5、6、7这几个出现的点可能会疑惑,实际上就是将边拆分成两个部分,即将1-3边拆分成4、5,而4与1是有关的、5与3是有关以此类推。
拆完之后 图是这样的,然后直接跑带花树
#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
#include <cstdlib>
#define INF 0x3f3f3f3f3f3f3f3f
#define inf 0x3f3f3f3f
#define FILL(a,b) (memset(a,b,sizeof(a)))
#define re register
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(a) ((a)&-(a))
#define ios std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
#define fi first
#define rep(i,n) for(int i=0;(i)<(n);i++)
#define rep1(i,n) for(int i=1;(i)<=(n);i++)
#define se second
#define scd(a) scanf("%d",&a)
#define scdd(a,b) scanf("%d%d",&a,&b)
#define scddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define ac cout<<ans<<"\n"
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> pii;
int dx[4]= {-1,1,0,0},dy[4]= {0,0,1,-1};
const ll mod=1e9+7;
const ll N =400;
const ll M =25000;
const double eps = 1e-4;
//const double pi=acos(-1);
ll qk(ll a,ll b){ll ans=1;while(b){if(b&1) ans=(ans*a)%mod;a=(a*a)%mod;b/=2;}return ans%mod;}int n,m;
int d[N];
int sum[N];
namespace BO{int n;int dfn[M];int low[N], fa[N];int tim;int pre[N];int match[N];queue<int>q;int h[N], to[M], nex[M], idx;int vis[N];int ans;void add(int x, int y,int o){to[idx] = y;nex[idx] = h[x];h[x] = idx ++ ;if(o) add(y,x,0);}int Find(int x){if(fa[x] == x)return x;return fa[x] = Find(fa[x]);}//朴素的lca,根据建图的方式暴力跳int lca(int x, int y){++ tim;//时间戳 / 缩点的编号while(dfn[x] != tim){dfn[x] = tim;//每走一步就标记一下, 如果遇见一个点已经被标记过了就说明这个点已经被另一个点走过了,也就说明是lcax = Find(pre[match[x]]);if(y)swap(x, y);}return x;}//开花(缩环)
//x和y是要开花的两个点(此时x和y都是黑点刚找到奇环的时候)
//w是他们的lca
//整个花缩完之后就是一个黑点// !这里虽然是两个点但是只是从x走到了x和y的lca:w,y到w路径上的点并没有染色或者丢到队列里去,所以要开两次,一次x一次y。void blossom(int x, int y, int w){while(Find(x) != w){pre[x] = y;y = match[x];//如果是白点就直接染成黑色,然后丢到队列里面vis[y] = 1, q.push(y);if(Find(x) == x) fa[x] = w;if(Find(y) == y) fa[y] = w;x = pre[y];}}int aug(int s){if((ans + 1) * 2 > n)return 0;for(int i = 1; i <= n; ++ i)fa[i] = i, pre[i] = vis[i] = 0;while(!q.empty()) q.pop();q.push(s);vis[s] = 1;//从黑点开始//队列中都是黑点,所以遇到相邻未染色的点都染成白点while(!q.empty()){int x = q.front();q.pop();for(int i = h[x] ; ~i; i = nex[i]){int y = to[i];//如果相邻点是白点,或者是同一朵花中的节点,则直接跳过这个点if(Find(x) == Find(y) || vis[y] == 2)continue;if(!vis[y]) { //未染色(匹配)点,说明找到了增广路vis[y] = 2;//没染色就把它染成白色pre[y] = x;if(!match[y]){int u = y;while(u){int v = pre[u], z = match[v];match[u] = v;match[v] = u;u = z;}return 1;}vis[match[y]] = 1, q.push(match[y]);}else{int w = lca(x, y);blossom(x, y, w);blossom(y, x, w);}}}return 0;}inline void main(int _n){n=_n;for(int i=1;i<=n;i++)if(!match[i]) ans+=aug(i);}inline void init(){memset(match, 0, sizeof match);memset(h,-1,sizeof(h));memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));memset(pre,0,sizeof(pre));tim=0;idx=0;ans=0;}
}
void sovle(){while(cin>>n>>m){int deg=0;for(int i=1;i<=n;i++) {cin>>d[i];deg+=d[i];sum[i]=sum[i-1]+d[i-1];}BO::init();int cnt=sum[n]+d[n];for(int i=1;i<=m;i++){int u,v;cin>>u>>v;BO::add(cnt+1,cnt+2,1);//拆边for(int j=1;j<=d[u];j++) BO::add(cnt+1,sum[u]+j,1);for(int j=1;j<=d[v];j++) BO::add(cnt+2,sum[v]+j,1);cnt+=2;}BO::main(cnt);int k=BO::ans;// cout<<cnt<<" "<<k<<endl;if((deg)%2==0&&k*2==cnt) cout<<"Yes\n";else cout<<"No\n";}
}
int main()
{
#ifdef LOCALfreopen("in.txt", "r", stdin);
#elseiosint t=1;//cin>>t;while(t--) sovle();
#endif // LOCALreturn 0;
}
参考:大神题解