[WC2016]:挑战NPC

传送门
这题还是很妙的
话说,我写带花树还是挺熟练的呀qwq
这题大概就是考虑如何让筐体现出它的价值
首先每个筐拆成三个点,分别代表三个空位
然后对于每个球可以放入框的对应关系,我们连三条边
最后再每个筐的三个点之间互相连边
如下图所示:(图是我拿来的)

这样来个带花树就做出来了
当然最后答案要-n
而且输出方案注意一下
还有边表开够
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define LL long long
using namespace std;
inline int read(){
    int x=0,f=1;char ch=' ';
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
const int N=605,M=5e5;
int tot,cnt,top,sum;
int head[M],to[M],Next[M];
inline void addedge(int x,int y){to[++tot]=y;Next[tot]=head[x];head[x]=tot;}
inline void ins(int x,int y){addedge(x,y);addedge(y,x);}
int f[N],vis[N],pre[N],nxt[N],q[N],s[N];
inline int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
inline int lca(int x,int y){
    for(cnt++,x=find(x),y=find(y);;swap(x,y))
        if(x){
            if(vis[x]==cnt)return x;
            vis[x]=cnt;
            x=find(pre[nxt[x]]);
        }
}
inline void blossom(int x,int y,int flower){
    while(find(x)!=flower){
        pre[x]=y;
        if(s[nxt[x]]==1)s[q[++top]=nxt[x]]=0;
        if(f[x]==x)f[x]=flower;
        if(f[nxt[x]]==nxt[x])f[nxt[x]]=flower;
        y=nxt[x];
        x=pre[y];
    }
}
inline int match(int st){
    for(int i=1;i<=sum;++i)f[i]=i;
    memset(s,-1,sizeof(s));
    s[q[top=1]=st]=0;int x;
    for(int l=1;l<=top;++l)
        for(int i=head[x=q[l]];i;i=Next[i]){
            int u=to[i];
            if(s[u]==-1){
                pre[u]=x;
                s[u]=1;
                if(!nxt[u]){
                    for(int v=x,lst;v;u=lst,v=pre[u])
                        lst=nxt[v],nxt[v]=u,nxt[u]=v;
                    return 1;
                }
                s[q[++top]=nxt[u]]=0;
            }
            else if(!s[u] && find(x)!=find(u)){
                int flower=lca(x,u);
                blossom(u,x,flower);
                blossom(x,u,flower);
            }
        }
    return 0;
}
inline int solve(){
    int ans=0;
    for(int i=1;i<=sum;++i)if(!nxt[i])ans+=match(i);
    return ans;
}
int T,n,m,E;
int main(){
    T=read();
    for(int s=1;s<=T;++s){
        n=read();m=read();E=read();
        sum=n+m*3;tot=0;cnt=0;
        memset(head,0,sizeof(head));
        memset(nxt,0,sizeof(nxt));
        memset(pre,0,sizeof(pre));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=E;++i){
            int u=read(),v=read();
            ins(u,n+v);
            ins(u,n+m+v);
            ins(u,n+(m<<1)+v);
        }
        for(int i=1;i<=m;++i){
            ins(n+i,n+m+i);
            ins(n+i,n+(m<<1)+i);
            ins(n+m+i,n+(m<<1)+i);
        }
        printf("%d\n",solve()-n);
        for(int i=1;i<=n;++i)printf("%d ",(nxt[i]-n-1)%m+1);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值