[Contest Hunter#17-C] 舞动的夜晚

Posted evenbao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Contest Hunter#17-C] 舞动的夜晚相关的知识,希望对你有一定的参考价值。

[题目链接]

          http://contest-hunter.org:83/contest/CH%20Round%20%2317/%E8%88%9E%E5%8A%A8%E7%9A%84%E5%A4%9C%E6%99%9A

[算法]

         不难发现,本题是要我们求出二分图最大匹配的不可行边,我们可以将此问题转化为求可行边的补集

         那么,怎样求二分图的可行边? 我们可以先来考虑一个简化的情况 : 二分图的最大匹配为完备匹配 

         我们求出任意一组二分图最大匹配,将匹配边(x,y)看作y到x的有向边,将非匹配(y,x)看作x到y的有向边,若x到y有增广路,则在新图G‘中x到y存在路径

         此时,若边(x,y)为可行边,则 :(x,y)当前为匹配边,或当前x匹配u,y匹配v,我们让x匹配y,u和v失去匹配,但我们可以找到一条从u到v的增广路

         仔细观察,我们发现(x,y)为可行边等价于在新图G‘上x和y在同一个强连通分量中

         在本题中,并没有保证最大匹配为完备匹配,但我们可以借助网络流的源点和汇点,不妨先运行dinic算法,然后,判定条件就变成了 : 在残余网络上,

         若x和y在同一个强连通分量内,则(x,y)为可行边

         时间复杂度 : O(T * Sqrt(N + M)) ( 其中,Sqrt表示开方 )

[代码]

          

#include<bits/stdc++.h>
using namespace std;
#define MAXN 10010
#define MAXT 200010 
const int inf = 2e9;

struct edge
{
        int to,w,id,nxt;
} e[MAXT << 1];

int i,p,n,m,t,S,T,timer,tot,cnt,scc,q,w,id,top;
int head[MAXN << 1],u[MAXT],v[MAXT],depth[MAXN << 1],s[MAXN << 1],
        belong[MAXN << 1],ans[MAXT],low[MAXN << 1],dfn[MAXN << 1];
bool flag[MAXT],instack[MAXN << 1];

inline void addedge(int u,int v,int id)
{
        tot++;
        e[tot] = (edge){v,1,id,head[u]};
        head[u] = tot;
        tot++; 
        e[tot] = (edge){u,0,id,head[v]};
        head[v] = tot;
}
inline bool bfs()
{
        int i,l,r,u,v,w;
        static int q[MAXN << 1];
        memset(depth,0,sizeof(depth));
        q[l = r = 1] = S;
        depth[S] = 1;
        while (l <= r)
        {
                u = q[l];
                l++;
                for (i = head[u]; i; i = e[i].nxt)
                {
                        v = e[i].to;
                        w = e[i].w;
                        if (!depth[v] && w)
                        {
                                depth[v] = depth[u] + 1;
                                q[++r] = v;
                                if (v == T) return true;
                        }
                }
        }        
        return false;
}
inline int dinic(int u,int flow)
{
        int i,v,w,rest = flow,k;
        if (u == T) return flow;
        for (i = head[u]; i && rest; i = e[i].nxt)
        {
                v = e[i].to;
                w = e[i].w;
                if (depth[v] == depth[u] + 1 && w)
                {
                        k = dinic(v,min(rest,w));
                        if (!k) depth[v] = 0;
                        e[i].w -= k;
                        e[i ^ 1].w += k;
                        rest -= k;
                }
        }
        return flow - rest;
}
inline void tarjan(int u)
{
        int i,v,w;
        low[u] = dfn[u] = ++timer;
        instack[u] = true;
        s[++top] = u;
        for (i = head[u]; i; i = e[i].nxt)
        {
                v = e[i].to;
                w = e[i].w;
                if (!w) continue;
                if (!dfn[v])
                {
                        tarjan(v);
                        low[u] = min(low[u],low[v]);
                } else if (instack[v]) low[u] = min(low[u],dfn[v]);
        }
        if (dfn[u] == low[u])
        {
                scc++;
                do
                {
                        v = s[top]; 
                        top--;
                        belong[v] = scc;
                        instack[v] = false;
                } while (v != u);
        }
}
int main() 
{
        
        scanf("%d%d%d",&n,&m,&t);
        for (i = 1; i <= t; i++) scanf("%d%d",&u[i],&v[i]);
        tot = 1;
        S = n + m + 1;
        T = n + m + 2;
        for (i = 1; i <= n; i++) addedge(S,i,0);
        for (i = 1; i <= t; i++) addedge(u[i],v[i] + n,i);
        for (i = 1; i <= m; i++) addedge(n + i,T,0);
        while (bfs())
        {
                while (dinic(S,inf));    
        }
        memset(flag,true,sizeof(flag));
        for (p = 1; p <= n; p++)
        {
                for (i = head[p]; i; i = e[i].nxt)
                {
                        q = e[i].to;
                        w = e[i].w;
                        id = e[i].id;
                        if (q > n && q <= n + m && !w) 
                                flag[id] = false; 
                }
        }
        for (i = 1; i <= n + m + 2; i++) 
        {
                if (!dfn[i])
                        tarjan(i);
        }
        for (i = 1; i <= t; i++) 
        {
                if (belong[u[i]] == belong[v[i] + n]) 
                        flag[i] = false;
        }
        for (i = 1; i <= t; i++) 
        {
                if (flag[i])
                        ans[++cnt] = i;
        }
        printf("%d
",cnt);
        for (i = 1; i <= cnt; i++) printf("%d ",ans[i]);
        printf("
");
        
        return 0;
    
}

 

以上是关于[Contest Hunter#17-C] 舞动的夜晚的主要内容,如果未能解决你的问题,请参考以下文章

CH#17C 舞动的夜晚(Dinic+tarjan)

Contest Hunter 0103 最短Hamilton路径 - 状压DP

Contest Hunter CH6201 走廊泼水节 最小生成树 Kruskal

Contest Hunter Adera6C 網絡升級 樹的直徑 樹形DP

单调队列

金字塔