牛客多校第一场补题 1 or 2 建图,一般图匹配 带花树

1 or 2

题目链接

题目大意

给一个无向图,给一个长度为n的a数组
问能不能删除一些边以后能不能让每个点的度数为ai

题解

比赛的时候,因为数据水,用网络流,乱水过去了,后来才知道有反例,不能跑网络流。
正解:
建图方式:拆点,把这个点拆成ai个点,如果有一个边x- - - - -y
就在图中新建两个点代表x,y 就把 x拆的点与x相连 y拆的点与y相连,再把x,y相连。
建完图之后,跑一个带花树板子就好了。
如果是完美匹配,那么就yes,否则是no
为什么这样建图是对的呢?
因为建完图之后会发现,随便提溜起来一条边,都可以向两边拆点中匹配一条边,并且只能匹配一次,如果一条边只有一边的度数为1那么另一边一定没有连点,一定匹配不到,那么就是no

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <unordered_map>
#include <cmath>
#include <set>
#include <cstring>
#include <string>
#include <bitset>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
typedef pair<int,ll> pii;
typedef unsigned long long ull;
typedef set<int>::iterator sit;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void wenjian(){freopen("concatenation.in","r",stdin);freopen("concatenation.out","w",stdout);}
void tempwj(){freopen("hash.in","r",stdin);freopen("hash.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct cmp{bool operator()(const pii & a, const pii & b){return a.second > b.second;}};
int lb(int x){return  x & -x;}
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;

const int M = 4e4+2;
const ll mod = 2012;
const int maxn = 555;

int head[maxn];
struct Edge
{
    int id,nex;
}edge[maxn * maxn * 2];
int cnt = 0;
void add(int x,int y)
{
    edge[cnt].id = y;
    edge[cnt].nex = head[x];
    head[x] = cnt ++ ;
}
/* 并查集维护 */
int fa[maxn];
int findx(int x)
{
    if(fa[x] == x)
        return x;
    return fa[x] = findx(fa[x]);
}

int link[maxn];// 记录路径
int vis[maxn]; // 标记层数是奇数或偶数
int mate[maxn];// 匹配的点
//queue<int> qq;// bfs找增广路用的队列
int qq[maxn <<2];int hd,tl;
int ss[maxn], tim;
int lca(int x,int y)
{
    ++tim;
    while(ss[x] != tim)
    {
        if(x)
        {
            ss[x] = tim;
            x = findx(link[mate[x]]);
        }
        swap(x,y);
    }
    return x;
}

void flower(int x,int y,int p)
{
    while(findx(x) != p)
    {
        link[x] = y;
        y = mate[x];
        fa[y] = fa[x] = p;
        if(vis[y] == 1)
        {
//            qq.push(y);
            qq[tl++] = y;
            vis[y] = 2;
        }
        x = link[y];
    }
}
int pnum;
bool match(int x) // 找增广路 匹配。
{
    hd = 0, tl = 0;// 队列的两个指针
    for (int  i= 1; i <= pnum; i ++ )
    {
        fa[i] = i;
        vis[i] = 0;
    }
    vis[x] = 2; // 标记为偶数层
    qq[tl ++ ] = x;
    while(tl > hd)
    {
//        x = qq.front();qq.pop();
        x = qq[hd];
//        cout<<x<<endl;
        hd ++ ;
        for(int i = head[x]; ~i; i = edge[i].nex)
        {
            int v = edge[i].id;
//            cout<<v<<mate[v]<<vis[v]<<endl;
            if(vis[v] == 0)// 还没走过
            {
                vis[v] = 1;
                link[v] = x;// 记录路径,是从x转移过来的
                if(mate[v] == 0)
                {
                    while(x)
                    {
                        x = mate[link[v]];
                        mate[v] = link[v];
                        mate[link[v]] = v;
                        v = x;
                    }
                    return true;
                }
                else
                {
//                    qq.push(mate[v]);
                    qq[tl++] = mate[v];
                    vis[mate[v]] = 2;
                }
            }
            else if(vis[v] == 2 && findx(v) != findx(x))
            {
                int p = lca(x,v);
                flower(x,v,p);
                flower(v,x,p);
            }
        }
    }
    return false;
}


int du[maxn];
vector<int> vv[maxn];
int main()
{
    int n,m;
    memset(head,-1,sizeof(head));
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        tim = 0;
        cnt = 0;
        pnum = 0;
        for (int i = 1; i <= n; i ++ )
        {
            scanf("%d",&du[i]);
            for (int j = 0; j < du[i]; j ++ )
            {
                vv[i].pb(++pnum);
            }
        }
        for (int i = 1; i <= m; i ++ )
        {
            int x,y;
            scanf("%d%d",&x,&y);
            ++pnum;
            for (int j = 0; j < vv[x].size(); j ++ )
            {
                add(pnum,vv[x][j]);
                add(vv[x][j],pnum);
//                cout<<vv[x][j]<<cnt<<endl;;
            }
            ++pnum;
            add(pnum,pnum - 1);
            add(pnum - 1,pnum);
//            cout<<cnt<<cnt - 1<<endl;
            for(int j = 0; j < vv[y].size(); j ++ )
            {
                add(pnum,vv[y][j]);
                add(vv[y][j],pnum);
//                cout<<vv[y][j]<<cnt<<endl;
            }
        }
        int f = 1;
        for (int i = 1; i <= pnum; i ++ )
        {
            if(mate[i] == 0 && (!match(i)))
            {
                f = 0;
                break;
            }
//            cout<<i<<mate[i]<<"111111111111"<<endl;;
        }
        if(f)
            printf("Yes\n");
        else
            printf("No\n");
        for(int i = 1; i<= pnum; i ++ )
        {
            ss[i] = 0;
            link[i] = 0;
            vv[i].clear();
            head[i] = -1;
            mate[i] = 0;
        }
    }
}

带花树板子,还没有完全懂。。 我好菜。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值