【JZOJ4376】染色配对

5 篇文章 0 订阅
2 篇文章 0 订阅

Description

这里写图片描述


就是给一些极大团包含点的关系,使最少的极大团包含点数为奇数。

Solution

这里题目说明每个点只与两个极大团有被包含关系,所以我们把极大团看成点,点看成边,则每个点连出去所有的边(它们所对应的点)都与这个大点(所对应的极大团)有被包含关系。

于是我们搜索每个连通块,对于每个连通块我们建一颗搜索树(如下图,黑色边代表搜索树上的边)。

这里写图片描述

对于每一个大点,我们把所有连出去的边对应的未被匹配过的点的编号记录下来,若记录的点个数为奇数,则它与它父亲连接的边所对应的点就留给父亲匹配,然后挨个输出即可。

举个例子:

点7连出去两条边编号为7、8,且都未被匹配。则(7,8)是一个匹配。

点4连出去四条边,未被匹配的有3、4、6(这里假设5已被匹配),则(4,6)是一个匹配,而多余的3留给点4的父亲点2匹配。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 20001
#define NN 400001
#define M 200001
using namespace std;
int m,n;
int to[NN],next[NN],last[NN],p[NN],num=0;
int z[N];
int zc[M];
bool pp[M];
void link(int x,int y,int t)
{
    num++;
    to[num]=y;
    next[num]=last[x];
    last[x]=num;
    p[num]=t;
}
bool bz[N];
int rt;
int find(int x)
{
    bz[x]=true;
    int t=z[x];
    for(int i=last[x];i;i=next[i])
    {
        int v=to[i];
        if(!bz[v]) t+=find(v);
    }
    return t;
}
int g[M];
void dfs(int x,int fa)
{
    bz[x]=true;
    int t=0;
    for(int i=last[x];i;i=next[i])
    {
        int v=to[i];
        if(!bz[v]) dfs(v,p[i]);
    }
    for(int i=last[x];i;i=next[i])
    if(!pp[p[i]] && p[i]!=fa) pp[p[i]]=true,g[++t]=p[i];
    if(rt!=x) pp[fa]=true,g[++t]=fa;
    int tt;
    fo(i,1,t)
    if(i%2==1) tt=g[i],pp[tt]=false;
    else
    {
        pp[g[i]]=pp[tt]=true;
        printf("%d %d\n",tt,g[i]);
    }
}
int main()
{
    cin>>m>>n;
    fo(i,1,n)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        z[x]++;
        link(x,y,i);
        link(y,x,i);
    }
    int ans=0;
    fo(i,1,m)
    if(!bz[i]) ans+=find(i)/2;
    cout<<ans<<endl;
    memset(bz,0,sizeof(bz));
    fo(i,1,m)
    if(!bz[i]) rt=i,dfs(i,0);
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值