题目链接
这题一眼看上去很懵逼,看到要使<=1的最大,还以为是拆边网络流,结果发现不行。
正解一般图最大匹配。
我们把一个袋子拆成三个点的一个环。
然后对于,我们从vi向ui对应的环的三个点连边。
跑最大匹配,答案就是
首先我们可以保证每一个球都有一个匹配。
因为我们可以从球开始找匹配,那么就肯定存在一个可行方案。
接着,我们考虑每一个环能够承受多少个球。
没有球的时候,这个篮子自己就会产生1的匹配。(因为与它相连的球不选这个环,那么这个环只能自己产生匹配。
有1个球的时候,其中一个点会与球产生匹配,另外两个点有边相连,自己也会产生1的匹配。
有2个球或者3个球的时候,自己本身不会产生匹配。
那么刚好,多出来的匹配正是半空篮子的个数。
证毕。
在求最大匹配的时候,我们先保证那些求"有篮可放”,再使篮子尽量多的产生匹配。
多出来的匹配正是.
#include
#include
#include
#include
#include
using namespace std;
int n,m,e;
int tot;
struct edge{
int y,next;
}s[1000010];
int first[1010],match[1010],fa[1010],pre[1010],tp[1010],tf[1010],tim=0,len=0;
queue f;
void ins(int x,int y){
s[++len]=(edge){y,first[x]};first[x]=len;
s[++len]=(edge){x,first[y]};first[y]=len;
}
int findpa(int x){
if(fa[x]!=x) return fa[x]=findpa(fa[x]);
return fa[x];
}
int Lca(int x,int y){
for(tim++;;swap(x,y))
if(x){
x=findpa(x);
if(tf[x]==tim) return x;
tf[x]=tim;x=pre[match[x]];
}
}
void make(int x,int y,int op){
while(findpa(x)!=op){
pre[x]=y;y=match[x];
if(tp[y]==2) tp[y]=1,f.push(y);
fa[x]=op;fa[y]=op;
x=pre[y];
}
}
bool Aug(int begin){
for(int i=1;i<=tot;i++) fa[i]=i,tp[i]=pre[i]=0;
while(!f.empty()) f.pop();
f.push(begin);
while(!f.empty()){
int x=f.front();f.pop();
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(findpa(x)==findpa(y) || tp[y]==2) continue;
if(!tp[y]){
tp[y]=2;pre[y]=x;
if(!match[y]){
for(int last;x;x=pre[last],y=last){
last=match[x];
match[x]=y;match[y]=x;
}
return true;
}
tp[match[y]]=1;f.push(match[y]);
}
else{
int op=Lca(x,y);
make(x,y,op);make(y,x,op);
}
}
}
return false;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
len=0;
scanf("%d %d %d",&n,&m,&e);tot=n+3*m;
for(int i=1;i<=tot;i++) first[i]=0,match[i]=0;
int x,y;
for(int i=1;i<=m;i++) ins(n+i,n+m+i),ins(n+m+i,n+m+m+i),ins(n+i,n+m+m+i);
for(int i=1;i<=e;i++){
scanf("%d %d",&x,&y);
ins(x,n+y);ins(x,n+m+y);ins(x,n+m+m+y);
}
int ans=0;
for(int i=1;i<=tot;i++) ans+=(!match[i] && Aug(i));
printf("%d\n",ans-n);
for(int i=1;i<=n;i++){
if(match[i]<=n+m) printf("%d ",match[i]-n);
else if(match[i]<=n+m+m) printf("%d ",match[i]-n-m);
else printf("%d ",match[i]-n-m-m);
}
printf("\n");
}
}
发现原来的带花树打法不行,就是在找Lca的那里。一定要按照permui大佬的打法。
另外,make函数中的继承父亲,我就直接继承了,好像还快了。