题目描述
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 di edges.

题意:

问能否保留一些边,使得第i个点连接di边(也就是第i个点的度为d[i])
理论上二分图匹配是不可以的。应该用带花树才可以,但因为数据问题,都可以做?
如果建图呢?
左侧,将第i个点分成d[i]份,当然编号不相同
右侧根据题目输入的u,v连边,然后将左右两边对应的点相连
就比如数据:

4 4
1 2 1 0
1 2
1 4
2 3
3 4

题目要求的答案
图片说明
图片说明
最大匹配结果是:
图片说明
为什么这样是对的呢?
如果存在完美匹配。也就是说每个点的每一度都有且仅有一条边与相应的端点相连。
左侧为拆点得到的点。右侧为拆边得到的边
左右侧之间的边说明相应该边对答案有贡献,应该保留的边,贡献为1
光右侧之间的边说明对答案并没有贡献,可以删去的

代码:

但是不知道代码哪里错了,。。。唉求大佬修改

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 510;
const int M = 3e5+10;
struct node{ int to,nxt; }g[M];
int head[N],cnt;
int vis[N],match[N],f[N],pre[N],Id,id[N];
//vis[i]: 0(未染色) 1(黑色) 2(白色)
//match[i]: i的匹配点
//f[i]: i在带花树中的祖先
//pre[i]: i的非匹配边的另一点
//id: 找LCA用
int n,m,ans,u,v;
queue<int> q;
void Init()
{
    Id=ans=cnt=0;
    for(int i=1;i<=n;i++)
        head[i]=-1,id[i]=match[i]=0;
}
void add(int u,int v){ g[cnt]=node{v,head[u]},head[u]=cnt++; }
int getf(int x){ return f[x]==x?x:f[x]=getf(f[x]); }
//查询x和y在带花树中的LCA
int LCA(int x,int y)
{
    //沿着增广路向上找lca
    for(++Id;;swap(x,y))//x,y交替向上
        if(x)
        {
            x=getf(x);//有可能环中有环(花中有花),所以用并查集找祖先,只处理祖先节点
            if(id[x]==Id) return x; //x,y在同一环中,一定会找到已被编号的点,该点即为LCA。
            else id[x]=Id,x=pre[match[x]];//给点编号,并沿着非匹配边向上找       
        }
}
//缩点(开花
void blossom(int x,int y,int l)//,将x、y到LCA(l)路径中的点,缩为一点int l)
{
    while(getf(x)!=l)
    {
        //增广路取反
        pre[x]=y;
        y=match[x];
        //如果x、y的奇环中有白点,将其染为黑点,放入队列,让其去找不是环中的匹配点   
        if(vis[y]==2) vis[y]=1,q.push(y);
        //只改变是根的点
        if(getf(x)==x) f[x]=l;
        if(getf(y)==y) f[y]=l;
        //增广路取反
        x=pre[y];
    }
}
bool aug(int s)
{
    //每次都以s为起点bfs,建带花树
    for(int i=1;i<=n;i++)
        vis[i]=pre[i]=0,f[i]=i;   
    while(!q.empty()) q.pop();

    q.push(s),vis[s]=1;
    while(!q.empty())
    {
        u=q.front();q.pop();
        for(int i=head[u];~i;i=g[i].nxt)
        {
            v=g[i].to;
            //如果已经在同一个环(花)中或者是白点(意为这已经有匹配点),只接跳过
            //这种情况不会增加匹配数
            if(getf(u)==getf(v)||vis[v]==2) continue;
            //如果没有被染色
            if(!vis[v])
            {
                //先染为白色,将前继点指向u
                vis[v]=2;
                pre[v]=u;
                //如果没有被匹配过,直接匹配成功
                if(!match[v])
                {
                    //增广路取反
                    for(int x=v,last;x;x=last)
                        last=match[pre[x]],match[x]=pre[x],match[pre[x]]=x;       
                    return 1;
                }
                //如果被匹配过,则把匹配v的点染为黑色,放入队列中   
                vis[match[v]]=1;
                q.push(match[v]);
            }
            //v是黑色,形成奇环,则缩点(开花)。
            else
            {
                int lca=LCA(u,v);
                blossom(u,v,lca);
                blossom(v,u,lca);
            }
        }   
    }
    return 0;
}
vector <int> de[N];
int main(void)
{
    while(~scanf("%d%d",&n,&m))    
    {
        Init();
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            int qqq;
            cin>>qqq;
            de[i].clear();
            while(qqq--)de[i].push_back(++sum);
        }
        n=sum+2*m;//所有的点数 
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            //用sum+1来代替u 
            add(sum+1,sum+2);
            add(sum+2,sum+1);
            for(auto x:de[u])
            {
                add(x,sum+1);
                add(sum+1,x);    
            }
            for(auto x:de[v])
            {
                add(x,sum+2);
                add(sum+2,x);
            }
            sum+=2;
        }
        for(int i=1;i<=n;i++)
            if(!match[i]&&aug(i))
                ans++;
        if(ans*2==n)
        printf("Yes\n");
        else
        printf("No\n");
    }
    return 0;    
}