cogs426 血帆海盗 最小割定理

链接:http://cogs.pro/cogs/problem/problem.php?pid=426

题意:在二分图中找出一类边的数量,这类边的特点是:砍掉这条边之后整个二分图最大匹配数量减少。

首先跑一个裸的二分图最大匹配,找到所有的连接方式。可能有人会认为最大匹配数就是所求结果,但其实不是,因为我们可以举出一个例子:

在这张图中,最大匹配数是1,但是显然种类是0种,因此我们还需要想其他的思路。

以样例为例,考虑跑完二分图匹配之后的增广路:

其中,虚线表示增广后的反向边。我们可以看出有些点是在同一个强连通分量上的,砍掉这些点之间的边毫无意义。

因此思路就出来了:跑完二分图最大匹配后在残量网络上尬舞跑Tarjan,搞出强连通分量,随后枚举每一条被增广的边,如果起点终点在同一强连通分量内,ans--。

问题得解。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 const int maxn=200005;
  7 struct node
  8 {
  9     int from,to,flow,next;
 10 }edge[maxn<<1];
 11 int head[maxn],tot;
 12 void addedge(int u,int v,int w)
 13 {
 14     edge[tot]=(node){u,v,w,head[u]};head[u]=tot++;
 15     edge[tot]=(node){v,u,0,head[v]};head[v]=tot++;
 16 }
 17 int n,m,S,T,exist[maxn];
 18 #include<queue>
 19 const int inf=0x7fffffff;
 20 int dis[maxn];
 21 bool bfs()
 22 {
 23     memset(dis,0,sizeof(dis));
 24     dis[S]=1;
 25     queue<int>q;q.push(S);
 26     while(!q.empty())
 27     {
 28         int u=q.front();q.pop();
 29         for(int i=head[u];i!=-1;i=edge[i].next)
 30         {
 31             int v=edge[i].to;
 32             if(!dis[v]&&edge[i].flow>0)
 33             {
 34                 dis[v]=dis[u]+1;
 35                 q.push(v);
 36             }
 37         }
 38     }
 39     return dis[T]!=0;
 40 }
 41 int dfs(int pos,int flow)
 42 {
 43     if(pos==T||!flow)return flow;
 44     int ans=0;
 45     for(int i=head[pos];i!=-1;i=edge[i].next)
 46     {
 47         int v=edge[i].to;
 48         if(dis[v]==dis[pos]+1&&edge[i].flow>0)
 49         {
 50             int t=dfs(v,min(flow,edge[i].flow));
 51             if(t)
 52             {
 53                 edge[i].flow-=t;
 54                 edge[i^1].flow+=t;
 55                 ans+=t;
 56                 if(ans==flow)break;
 57             }
 58         }
 59     }
 60     return ans;
 61 }
 62 int dfn[maxn],low[maxn],cnt,scnt,belong[maxn],instack[maxn];
 63 #include<stack>
 64 stack<int>s;
 65 void dfs(int pos)
 66 {
 67     dfn[pos]=low[pos]=++cnt;s.push(pos);instack[pos]=1;
 68     for(int i=head[pos];i!=-1;i=edge[i].next)
 69     {
 70         int v=edge[i].to;
 71         if(edge[i].flow>0)
 72         {
 73             if(!dfn[v])
 74             {
 75                 dfs(v);
 76                 low[pos]=min(low[pos],low[v]);
 77             }
 78             else if(instack[v])low[pos]=min(low[pos],dfn[v]);
 79         }
 80     }
 81     if(low[pos]==dfn[pos])
 82     {
 83         scnt++;int k;
 84         do
 85         {
 86             k=s.top();s.pop();
 87             belong[k]=scnt;
 88             instack[k]=0;
 89         }while(k!=pos);
 90     }
 91 }
 92 int haha()
 93 {
 94     freopen("bloodsail.in","r",stdin);
 95     freopen("bloodsail.out","w",stdout);
 96     memset(head,-1,sizeof(head));
 97     scanf("%d%d",&n,&m);
 98     for(int i=1;i<=m;i++)
 99     {
100         int x,y;scanf("%d%d",&x,&y);
101         addedge(x,y,1);
102         exist[x]=exist[y]=1;
103     }
104     S=0,T=n+1;
105     int mid=n>>1;
106     for(int i=1;i<=mid;i++)
107         if(exist[i])addedge(S,i,1);
108     for(int i=mid+1;i<=n;i++)
109         if(exist[i])addedge(i,T,1);
110     int ans=0;
111     while(bfs())
112         ans+=dfs(S,inf);
113     for(int i=1;i<=n;i++)
114         if(!dfn[i])dfs(i);
115     for(int i=1;i<=mid;i++)
116         for(int j=head[i];j!=-1;j=edge[j].next)
117         {
118             int v=edge[j].to;
119             if(!edge[j].flow&&v&&belong[i]==belong[v])ans--;
120         }
121     printf("%d
",ans);
122 }
123 int sb=haha();
124 int main(){;}
cogs426
只要是活着的东西,就算是神我也杀给你看。
原文地址:https://www.cnblogs.com/Loser-of-Life/p/7261482.html