您的位置:首页 > 其它

挑战NPC

2016-02-10 17:21 211 查看

挑战NPC

时间限制: 1 Sec 内存限制: 256 MB

题目描述

传送门:http://uoj.ac/problem/171

ps.bzoj上的题面有毒,不用输出方案。

题解

神建图,orzvfk!!!

把每个筐子拆成三个点,将这三个点相互连边(其实只要连一条就可以)。对于每个球,向能与它连边的所有筐子的每一个点连边,然后跑最大匹配即可。

先简单证明一下:如果一个筐子只放一个球或不放球,那么代表这个筐子的三个点就必定会有两个点形成一组匹配。若一个筐子放球数超过1个,则显然无法形成匹配。

最大匹配怎么写?不是二分图,匈牙利、网络流就行不通了。

别急,我们还有带花树(orzorz)!

至于带花树怎么写,网上资料很多,本蒟蒻就不再赘述了。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#define N 610
#define M 200000
using namespace std;
int T,n,m,e,ans,fa
,mate
,flag
,parent
;
int k,la
,ff[M],l,r,q
,cnt,check
;
struct node{int a,b;}map[M];

void add(int a,int b)
{
map[++k]=(node){a,b};ff[k]=la[a];la[a]=k;
map[++k]=(node){b,a};ff[k]=la[b];la[b]=k;
}

int find(int x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}

int lca(int x,int y)
{
x=find(x);y=find(y);cnt++;
while(1)
{
if(check[x]==cnt)return x;
if(x)check[x]=cnt,x=find(parent[mate[x]]);
if(check[y]==cnt)return y;
if(y)check[y]=cnt,y=find(parent[mate[y]]);
}
}

void merge(int x,int y,int t)
{
while(find(x)!=t)
{
parent[x]=y;
if(flag[mate[x]]==2)
q[r]=mate[x],flag[q[r]]=1,r++;
if(find(x)==x)fa[x]=t;
if(find(mate[x])==mate[x])fa[mate[x]]=t;
y=mate[x];x=parent[y];
}
}

int bfs(int S)
{
for(int i=1;i<=n+m*3;i++)fa[i]=i,flag[i]=0;
l=1;r=2;q[1]=S;flag[S]=1;parent[S]=0;
while(l<r)
{
int x=q[l];l++;
for(int a=la[x];a;a=ff[a])
{
int y=map[a].b;
if(!flag[y])
{
parent[y]=x;flag[y]=2;
if(!mate[y])
{
while(y)
{
int t=mate[parent[y]];
mate[y]=parent[y];
mate[parent[y]]=y;y=t;
}
return 1;
}
q[r]=mate[y];flag[q[r]]=1;r++;continue;
}
if(flag[y]==1&&find(x)!=find(y))
{
int t=lca(x,y);
merge(x,y,t);merge(y,x,t);
}
}
}
return 0;
}

int main()
{
int a,b;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&e);ans=0;
k=0;memset(la,0,sizeof(la));
memset(parent,0,sizeof(parent));
memset(mate,0,sizeof(mate));
for(int i=1;i<=e;i++)
{
scanf("%d%d",&a,&b);
add(a,n+b);add(a,n+m+b);add(a,n+m*2+b);
}
for(int i=1;i<=m;i++)add(n+i,n+m+i);
for(int i=1;i<=n+m*3;i++)
if(!mate[i])ans+=bfs(i);
printf("%d\n",ans-n);
for(int i=1;i<=n;i++)
{
mate[i]-=n;
if(mate[i]%m)printf("%d ",mate[i]%m);
else printf("%d ",m);
}
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: