BZOJ4405: [wc2016]挑战NPC

Orz vfk
将每个框拆成3个点,如果一个球x可以放进一个框y中,就将x和y拆成的3个点连边,每个框拆的3个点互相连边,那么,
当一个框不和任何球匹配时,它贡献的匹配数是1
当一个框和1个球匹配时,它贡献的匹配数是2
当一个框和2个球匹配时,它贡献的匹配数是2
当一个框和3个球匹配时,它贡献的匹配数是3
这样,可得 匹配数-球数=放的球数≤1的框数
因为是无向图,球数不变,所以求无向图最大匹配-球数就是答案

vfk在题解中提到,通过调整建图,≤2,≥1,≥2都是可做的
≤2似乎可以把一个框拆成4个点,球向3个点连边,第4个点向其他三个点连边
其他情况我就不会了,如果有人会,如果能留言告诉我,我将感激不尽

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long 
using namespace std;

int read()
{
    char c; int x;
    while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9')(x*=10)+=c-'0';
    return x;
}
void swap(int &x,int &y){x^=y;y^=x;x^=y;}
const int maxn = 1010;
const int maxm = 331000;

struct edge
{
    int y,nex;
    edge(){}
    edge(const int &_y,const int &_nex){y=_y;nex=_nex;}
}a[maxm]; int len,fir[maxn];
int n,n1,n2,m;
void ins(const int &x,const int &y)
{
    a[++len]=edge(y,fir[x]); fir[x]=len;
    a[++len]=edge(x,fir[y]); fir[y]=len;
}

int fa[maxn];
int find_(const int &x)
{
    if(fa[x]==x) return x;
    return fa[x]=find_(fa[x]);
}
void Together(const int &x,const int &y)
{
    int f1=find_(x),f2=find_(y);
    if(f1!=f2) fa[f1]=f2;
}

int nex[maxn],mark[maxn],spouse[maxn];
int vis[maxn],ti;
int LCA(int x,int y)
{
    ti++;
    while(true)
    {
        if(x!=-1)
        {
            x=find_(x);
            if(vis[x]==ti) return x;
            vis[x]=ti;
            if(spouse[x]!=-1) x=nex[spouse[x]];
            else x=-1;
        }
        swap(x,y);
    }
}
int que[maxn],tail;
void group(int x,const int &p)
{
    while(x!=p)
    {
        int b=spouse[x],c=nex[b];
        if(find_(c)!=p) nex[c]=b;
        if(mark[b]==2) mark[que[++tail]=b]=1;
        if(mark[c]==2) mark[que[++tail]=c]=1;
        Together(x,b);
        Together(b,c);
        x=c;
    }
}

void augment(const int &s)
{
    for(int i=1;i<=n;i++) nex[i]=-1,mark[i]=0,fa[i]=i;
    que[tail=1]=s;  mark[s]=1;
    for(int i=1;i<=tail&&spouse[s]==-1;i++)
    {
        const int x=que[i];
        for(int k=fir[x];k;k=a[k].nex)
        {
            const int y=a[k].y;
            if(find_(x)!=find_(y)&&spouse[x]!=y&&mark[y]!=2)
            {
                if(spouse[y]==-1)
                {
                    nex[y]=x;
                    for(int j=y;j!=-1;)
                    {
                        int b=nex[j],c=spouse[b];
                        spouse[b]=j;
                        spouse[j]=b;
                        nex[b]=c;
                        j=c;
                    }
                    break;
                }
                else if(mark[y]==1)
                {
                    const int p=LCA(x,y);
                    if(find_(x)!=p) nex[x]=y;
                    if(find_(y)!=p) nex[y]=x;
                    group(x,p);
                    group(y,p);
                }
                else
                {
                    nex[y]=x;
                    mark[y]=2;
                    mark[que[++tail]=spouse[y]]=1;
                }
            }
        }
    }
}

int main()
{
    int t=read();
    while(t--)
    {
        memset(fir,0,sizeof fir); len=0;
        memset(vis,0,sizeof vis); ti=0;

        n1=read(); n2=read(); m=read();
        int N=n2*3;
        n=n1+N;
        for(int i=1,id=1;i<=n2;i++,id+=3)
        {
            ins(id,id+1);
            ins(id,id+2);
            ins(id+1,id+2);
        }
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read();
            x+=N; y=(y-1)*3+1;
            ins(x,y); ins(x,y+1); ins(x,y+2);
        }

        memset(spouse,-1,sizeof spouse);
        for(int i=n;i>=1;i--) 
            if(spouse[i]==-1) augment(i);

        int re=0;
        for(int i=1;i<=n;i++) if(spouse[i]!=-1) re++;
        printf("%d\n",(re>>1)-n1);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值