3月7日二分图入门学习总结

今天主要在学习二分图入门
3道题
1:CF19E Fairy
传送门
题面一大串经过人性 翻译后便是:
给定 n 个点,m 条边的无向图,可以从图中删除一条边,问删除哪条边可以使图变成一个二分图。

因为要从普通的图到二分图,所以第一步考虑二分图与普通图区别即其特性:
二分图中所有环的长度均是偶数

所以我们现在目标是:找出图中所有奇环的交,也就是说,所有奇环的交就是答案。因为虽然答案不止一条边,但要求是只删这一条便能满足,所以必须是所有奇环的交。如果没有奇环就好办了,那每条边都可以删。

实现方式:
用dfs找环,用tag[u]数组记录u点在多少奇环上,每找到u点到v点之间有一个奇环,便将u点到v点之间更新tag值,很显然用树上差分便能实现,统计奇环个数,最后再dfs查找tag值与统计值zt相同的点,更新答案。

上代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<list>
#include<queue>
#include<stack>
#include<bitset>
#include<deque>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define ri register int
#define il inline
#define fi first
#define se second
#define mp make_pair
#define pi pair<int,int>
#define mem0(x) memset((x),0,sizeof (x))
#define mem1(x) memset((x),0x3f,sizeof (x))
#define gc getchar
#define pb push_back
template<class T>void in(T &x)
{
    x = 0; bool f = 0; char c = gc();
    while (c < '0' || c > '9') {if (c == '-') f = 1; c = gc();}
    while ('0' <= c && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = gc();}
    if (f) x = -x;
}
#undef gc
void out(int x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) out(x / 10);
    putchar(x % 10 + '0');
}
#define N 10010
#define M N<<2
int n, m;
int v[M], u[M], nx[M], k[M];
int cnt = 1, head[N];
il void add(int uu, int vv) {
    u[++cnt] = uu, v[cnt] = vv, nx[cnt] = head[uu];
    head[uu] = cnt;
    k[cnt] = (cnt >> 1);
}
int dep[N];
bool vis[N];
int tag[N];
int zt;
vector<int>ans;
int lst;
void dfs(int x, int f, int d) {
    //printf("V %d\n",x);
    dep[x] = d;
    vis[x] = 1;
    for (ri i = head[x]; i; i = nx[i]) {
        if (i == f) continue;
        if (!vis[v[i]]) {
            dfs(v[i], i ^ 1, d + 1);
        }
        else {
            if (dep[v[i]] < dep[x]) continue;
            if ((dep[v[i]] - dep[x] + 1) & 1) {
                tag[x]--;  // in fact +1 -2
                tag[v[i]]++;
                zt++;
                lst=k[i];
            }
            else {
                tag[x]++;
                tag[v[i]]--;
            }
        }
    }
}
int dfs2(int x, int f) {
    vis[x] = 1;
    int res = tag[x];
    for (ri i = head[x]; i; i = nx[i]) {
        if (vis[v[i]]) continue;
        res += dfs2(v[i], i);
    }
    if (res == zt) ans.pb(k[f]);
    //printf("T %d %d %d\n",x,res,tag[x]);
    return res;
}
signed main() {
    in(n), in(m);
    for (ri i = 1, a, b; i <= m; ++i) {
        in(a), in(b);
        add(a, b);
        add(b, a);
    }
    for (ri i = 1; i <= n; ++i) {
        if (!vis[i]) dfs(i, 0, 0);
    }
    if (zt == 0) {
        //cout << "A";
        printf("%d\n", m);
        for (ri i = 1; i <= m; ++i) printf("%d ", i);
        return 0;
    }
    if(zt==1) ans.pb(lst);
    //cout<<zt;
    mem0(vis);
    for (ri i = 1; i <= n; ++i) {
        if (!vis[i]) dfs2(i, 0);
    }
    sort(ans.begin(), ans.end());
    printf("%d\n", ans.size());
    for (ri i = 0; i < ans.size(); ++i) {
        printf("%d ", ans[i]);
    }
    return 0;
}

ps:在这里插入图片描述根据树上差分的方法,应该在两个端点+1,LCA-2,而DFS树无横叉边,所以LCA就是深度较浅的点,将它+1-2即可;
2题3题都是二分图匹配模板题,用的匈牙利算法,代码如下。
Luogu p3386

#include<bits/stdc++.h>
using namespace std;
int n,m,e,first[1001],next[1001],to[1001],tot,match[1001],ans,x,y,whe[1001];
void add(int x,int y)
{
	tot++;
	next[tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}
bool dfs(int pos)
{
	for(int i=first[pos];i;i=next[i])
	{
		int v=to[i];
		if(!whe[v])
		{
			whe[v]=1;
			if(!match[v]||dfs(match[v]))
			{
			match[v]=pos;
			return true;
		}
		}
	}
	return false;
}
int main()
{
	scanf("%d%d%d",&n,&m,&e);
	for(int i=1;i<=e;i++)
	{
		scanf("%d%d",&x,&y);
		if(x>n||y>m)
		continue;
		add(x,y);
	}
	for(int i=1;i<=n;i++)
	{
		memset(whe,0,sizeof(whe));
	    if(dfs(i)) 
	    ans++;
	}
	cout<<ans;
}

luogu p2756

#include<bits/stdc++.h>
using namespace std;
int tot,n,m,way[101][201],match[201],whe[201],ans,x,y;
bool dfs(int pos)
{
	for(int i=n+1;i<=n+m;i++)
	{
		if(!whe[i]&&way[pos][i])
		{
			whe[i]=1;
			if(!match[i]||dfs(match[i]))
			{
				match[i]=pos;
				return true;
			}
		}
	} 
	return false;
}
struct Ans
{
	int l;
	int r;
}q[101];
bool comp(Ans a,Ans b)
{
	return a.l<b.l; 
}
int main()
{
	scanf("%d%d",&n,&m);
	while(scanf("%d%d",&x,&y))
	{
		if(x==-1)
		break;
		way[x][y]=1;
	}
	for(int i=1;i<=n;i++)
	{
		memset(whe,0,sizeof(whe));
		if(dfs(i))
		ans++;
	}
	cout<<ans<<endl;
	tot=0;
	for(int i=n+1;i<=n+m;i++)
	{
		if(match[i])
		{
			tot++;
			q[tot].l=match[i];
			q[tot].r=i;
		}
	}
	sort(q+1,q+1+tot,comp);
	for(int i=1;i<=tot;i++)
	{
		cout<<q[i].l<<" "<<q[i].r<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值