bzoj3546[ONTAK2010]Life of the Party

题意是裸的二分图关键点(必然在二分图最大匹配中出现的点).比较经典的做法在cyb15年的论文里有:

前几天写jzoj5007的时候脑补了一种基于最小割可行边的做法:考虑用最大流求解二分图匹配.如果某个点必须在最大匹配中出现,相当于删去这个点后最大匹配数目减少,即删去这个点和源/汇点的连边后剩下的网络中s到t的最小割减小.反之,如果删去这个点后最大匹配数目不变,那么删去这个点和源/汇的连边后网络的最小割不变.上述两个条件分别等价于是否存在一个原网络的最小割方案包含这个点和源/汇的连边,于是转化成网络流中的最小割可行边问题,可以通过在残量网络上用tarjan求SCC解决,即将残量网络上所有流量不为零的正向边和反向边取出,按照边原先的指向建图,求SCC,如果某条满流边连接的两个点在不同的强联通分量中说明这条边是最小割的可行边.这个做法写起来比较麻烦,不是很优越.

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20005,maxm=1000005;
struct edge{
  int to,next,w;
}lst[maxm],lst2[maxm];int len=0,first[maxn],_first[maxn],first2[maxn],len2=1;
void addedge(int a,int b,int w){
  lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++;
  lst[len].to=a;lst[len].next=first[b];lst[len].w=0;first[b]=len++;
}
void addedge2(int a,int b){
  lst2[len2].to=b;lst2[len2].next=first2[a];first2[a]=len2++;
  //  lst2[len2].to=a;lst2[len2].next=first2[b];lst2[len2].w=w;first2[b]=len2++;
}
int n1,n2,m;
int q[maxn],vis[maxn],dis[maxn],s,t,head,tail,T;
bool bfs(){
  head=tail=0;vis[s]=++T;dis[s]=1;q[tail++]=s;
  while(head!=tail){
    int x=q[head++];
    for(int pt=first[x];pt!=-1;pt=lst[pt].next){
      if(lst[pt].w&&vis[lst[pt].to]!=T){
    dis[lst[pt].to]=dis[x]+1;vis[lst[pt].to]=T;q[tail++]=lst[pt].to;
      }
    }
  }
  if(vis[t]==T)memcpy(_first,first,sizeof(first));
  return vis[t]==T;
}
int dfs(int x,int lim){
  if(x==t)return lim;
  int flow=0,a;
  for(int pt=_first[x];pt!=-1;pt=lst[pt].next){
    _first[x]=pt;
    if(lst[pt].w&&dis[lst[pt].to]==dis[x]+1&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){
      lst[pt].w-=a;lst[pt^1].w+=a;flow+=a;
      if(lim==flow)return flow;
    }
  }
  return flow;
}
int dinic(){
  int ans=0,x;
  while(bfs())while(x=dfs(s,0x7f7f7f7f))ans+=x;
  return ans;
}
int used[maxn];
int ans1[maxn],ans2[maxn];
int dfn[maxn],clk;
void Dfs(int x,int typ){
  dfn[x]=clk;
  for(int pt=first[x];pt!=-1;pt=lst[pt].next){
    if(lst[pt].w==typ&&dfn[lst[pt].to]!=clk)Dfs(lst[pt].to,typ);
  }
}
int main(){
  memset(first,-1,sizeof(first));
  scanf("%d%d%d",&n1,&n2,&m);
  int a,b;
  s=0;t=n1+n2+1;
  for(int i=1;i<=n1;++i)addedge(s,i,1);
  for(int i=1;i<=n2;++i)addedge(n1+i,t,1);
  for(int i=1;i<=m;++i){
    scanf("%d%d",&a,&b);addedge(a,n1+b,1);
  }
  dinic();
  for(int pt=first[s];pt!=-1;pt=lst[pt].next){
    if(lst[pt].w==0)used[lst[pt].to]=1;
  }
  for(int pt=first[t];pt!=-1;pt=lst[pt].next){
    if(lst[pt].w==1)used[lst[pt].to]=1;
  }
  ++clk;
  for(int i=1;i<=n1;++i){
    if(!used[i])Dfs(i,1);
  }
  for(int i=1;i<=n1;++i){
    if(used[i]&&dfn[i]!=clk)ans1[i]=1;
  }
  ++clk;
  for(int i=1;i<=n2;++i){
    if(!used[n1+i])Dfs(n1+i,0);
  }
  for(int i=n1+1;i<=n1+n2;++i){
    if(used[i]&&dfn[i]!=clk)ans2[i]=1;
  }
  for(int i=1;i<=n1;++i)if(ans1[i])printf("%d\n",i);
  for(int i=1;i<=n2;++i)if(ans2[i+n1])printf("%d\n",i);
  return 0;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20005,maxm=1000005;
struct edge{
  int to,next,w;
}lst[maxm],lst2[maxm];int len=0,first[maxn],_first[maxn],first2[maxn],len2=1;
void addedge(int a,int b,int w){
  lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++;
  lst[len].to=a;lst[len].next=first[b];lst[len].w=0;first[b]=len++;
}
void addedge2(int a,int b){
  lst2[len2].to=b;lst2[len2].next=first2[a];first2[a]=len2++;
  //  lst2[len2].to=a;lst2[len2].next=first2[b];lst2[len2].w=w;first2[b]=len2++;
}
int n1,n2,m;
int q[maxn],vis[maxn],dis[maxn],s,t,head,tail,T;
bool bfs(){
  head=tail=0;vis[s]=++T;dis[s]=1;q[tail++]=s;
  while(head!=tail){
    int x=q[head++];
    for(int pt=first[x];pt!=-1;pt=lst[pt].next){
      if(lst[pt].w&&vis[lst[pt].to]!=T){
    dis[lst[pt].to]=dis[x]+1;vis[lst[pt].to]=T;q[tail++]=lst[pt].to;
      }
    }
  }
  if(vis[t]==T)memcpy(_first,first,sizeof(first));
  return vis[t]==T;
}
int dfs(int x,int lim){
  if(x==t)return lim;
  int flow=0,a;
  for(int pt=_first[x];pt!=-1;pt=lst[pt].next){
    _first[x]=pt;
    if(lst[pt].w&&dis[lst[pt].to]==dis[x]+1&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){
      lst[pt].w-=a;lst[pt^1].w+=a;flow+=a;
      if(lim==flow)return flow;
    }
  }
  return flow;
}
int dinic(){
  int ans=0,x;
  while(bfs())while(x=dfs(s,0x7f7f7f7f))ans+=x;
  return ans;
}
int ans1[maxn],ans2[maxn];
namespace Tarjan{
  int stack[maxn],top,dfn[maxn],low[maxn],T,belong[maxn],tot;
  bool ins[maxn];
  void dfs(int x){
    low[x]=dfn[x]=++T;ins[x]=true;stack[top++]=x;
    for(int pt=first2[x];pt;pt=lst2[pt].next){
      if(!dfn[lst2[pt].to]){
    dfs(lst2[pt].to);
    if(low[lst2[pt].to]<low[x])low[x]=low[lst2[pt].to];
      }else if(ins[lst2[pt].to]&&dfn[lst2[pt].to]<low[x])low[x]=dfn[lst2[pt].to];
    }
    if(dfn[x]==low[x]){
      ++tot;
      do{
    ins[stack[--top]]=false;
    belong[stack[top]]=tot;
      }while(stack[top]!=x);
    }
  }
  void tarjan(){
    for(int i=s;i<=t;++i){
      if(!dfn[i])dfs(i);
    }
  }
};
int main(){
  memset(first,-1,sizeof(first));
  scanf("%d%d%d",&n1,&n2,&m);
  int a,b;
  s=0;t=n1+n2+1;
  for(int i=1;i<=n1;++i)addedge(s,i,1);
  for(int i=1;i<=n2;++i)addedge(n1+i,t,1);
  for(int i=1;i<=m;++i){
    scanf("%d%d",&a,&b);addedge(a,n1+b,1);
  }
  dinic();
  for(int i=s;i<=t;++i){
    for(int pt=first[i];pt!=-1;pt=lst[pt].next){
      if(lst[pt].w){
    if(lst[pt^1].w)addedge2(i,lst[pt].to);
    else addedge2(i,lst[pt].to);
      }
    }
  }
  Tarjan::tarjan();
  using Tarjan::belong;
  for(int i=s;i<=t;++i){
    for(int pt=first[i];pt!=-1;pt=lst[pt].next){
      if(lst[pt].w==0&&belong[i]!=belong[lst[pt].to]){
    if(i==s&&lst[pt].to<=n1){
      ans1[lst[pt].to]=1;
    }
    if(lst[pt].to==t&&i>n1){
      ans2[i-n1]=1;
    }
      }
    }
  }
  for(int i=1;i<=n1;++i)if(ans1[i])printf("%d\n",i);
  for(int i=1;i<=n2;++i)if(ans2[i])printf("%d\n",i);
  return 0;
}

 

 

转载于:https://www.cnblogs.com/liu-runda/p/6530858.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值