2013 ACM/ICPC Asia Regional Changsha Online - I Grand Prix

离正解非常非常接近就差一步的感觉真让人忧伤_(:3」∠)_

第一步肯定是求出最大匹配,数据范围挺大,连Dinic都超时,只能使用Hopcroft-Karp算法来求。


求出最大匹配ans后,先是一个朴素的想法。

枚举每条不是匹配的边(u,v),把它强行匹配,就是删去u,v两点,看新图的最大匹配数是否是ans-1,是的话它就可能出现在最大匹配中。

但这样的时间复杂度明显过大。


删去了u和v两点至多删去两个匹配,如果新图的匹配是ans-1就意味着u,v两点原来匹配的两点可以找到一条增广路径。

先假设用的就是dinic算法求的匹配,那么很明显的是(u,v)是最大匹配等价于有条从v到u的路,反之则有条从u到v的路。

所以每次询问u到v是否有路时,一定存在边(v,u),故问题等价于判断u与vv是否可以相互到达。


综上只需要把Hopcroft-Karp求出匹配的结果还原到网络流上,并在这个图上求出强连通分量,对于每条非匹配边,判断u和v不在一个SCC里面,

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <list>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
//#pragma comment(linker, "/STACK:16777216")
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
#define N 40005
#define M 1000005

int n , m , p , s , t , ans;
pair<int , int> ee[200000];
struct arc
{
  int x , next;
}e[M] , g[M];
int pre[N] , mcnt , tmp[N] , ncnt;

void addarc(int x ,int y)
{
  e[mcnt] = (arc) {y , pre[x]} , pre[x] = mcnt ++;
}
void addedge(int x ,int y)
{
  g[ncnt] = (arc) {y , tmp[x]} , tmp[x] = ncnt ++;
}

int mx[N] , my[N];
queue<int> que;
int dx[N] , dy[N];
bool vis[N];

bool find(int x)
{
    for (int i = pre[x] ; ~i ; i = e[i].next)
    {
        int y = e[i].x;
        if (!vis[y] && dy[y] == dx[x] + 1)
        {
            vis[y] = 1;
            if (!~my[y] || find(my[y]))
            {
                mx[x] = y , my[y] = x;
                return 1;
            }
        }
    }
    return 0;
}

int matching()
{
    memset(mx , -1 , sizeof(mx));
    memset(my , -1 , sizeof(my));
    int ans = 0;
    while (1){
        bool flag = 0;
        while (!que.empty()) que.pop();
        memset(dx , 0 , sizeof(dx));
        memset(dy , 0 , sizeof(dy));
        for (int i = 0 ; i < n ; ++ i)
            if (!~mx[i]) que.push(i);
        while (!que.empty())
        {
            int x = que.front(); que.pop();
            for (int i = pre[x] ; ~i ; i = e[i].next)
            {
                int y = e[i].x;
                if (!dy[y])
                {
                    dy[y] = dx[x] + 1 ;
                    if (~my[y])
                        que.push(my[y]) , dx[my[y]] = dy[y] + 1;
                    else
                        flag = 1;
                }
            }
        }
        if (!flag) break;
        memset(vis , 0 , sizeof(vis));
        for (int i = 0 ; i < n ; ++ i)
            if (!~mx[i] && find(i)) ++ ans;
    }
    return ans;
}

int idx , scnt , low[N] , DFN[N] , bel[N];
stack<int> st; bool is[N];
void tarjan(int x)
{
  int i , y;
  DFN[x] = low[x] = ++ idx;
  is[x] = 1 , st.push(x);
  for (i = tmp[x] ; ~i ; i = g[i].next)
  {
    y = g[i].x;
    if (!DFN[y])
    {
      tarjan(y);
      low[x] = min(low[x] , low[y]);
    }
    else if (is[y])
      low[x] = min(low[x] , DFN[y]);
  }
  if (DFN[x] == low[x])
  {
    scnt ++;
    do
    {
      i = st.top() , st.pop();
      is[i] = 0 , bel[i] = scnt;
    }while (x != i);
  }
}

char str[10];
vector<int> res;
void work()
{
  int i , j , x , y;
  scanf("%d%d",&m,&p);
  memset(pre , -1 , sizeof(pre)) , mcnt = 0;
  s = n + m , t = s + 1;
  for (i = 0 ; i < p ; ++ i)
  {
    scanf("%s" , str);
    x = y = 0;
    for (j = 0 ; j < 3 ; ++ j)
    {
      x <<= 5;
      if (isdigit(str[j]))
        x |= (str[j] - '0');
      else  x |= (str[j] - 'A' + 10);
    }
    for (j = 3 ; j < 6 ; ++ j)
    {
      y <<= 5;
      if (isdigit(str[j]))
        y |= (str[j] - '0');
      else  y |= (str[j] - 'A' + 10);
    }
    ee[i] = make_pair(x , y);
    addarc(x , n + y);
  }

  res.clear();
  ans = matching();

  memset(tmp , -1 , sizeof(tmp)) , ncnt = 0;
  for (i = 0 ; i < n ; ++ i)
    if (!~mx[i])
       addedge(s , i);
    else addedge(i , s);
  for (i = n ; i < n + m ; ++ i)
    if (!~my[i])
       addedge(i , t);
    else addedge(t , i);
  for (x = 0 ; x < n ; ++ x)
    for (i = pre[x] ; ~i ; i = e[i].next)
      if (mx[x] != e[i].x)
        addedge(x , e[i].x);
      else addedge(e[i].x , x);

  scnt = idx = 0;
  memset(low , 0 , sizeof(low));
  memset(DFN , 0 , sizeof(DFN));
  for (i = 0 ; i <= t ; ++ i)
    if (!DFN[i])
      tarjan(i);

  for (i = 0 ; i < p ; ++ i)
  {
    x = ee[i].fi , y = ee[i].se;
    if (mx[x] == y + n) continue;
    if (bel[x] != bel[y + n])
        res.push_back(i);
  }

  printf("%d\n" ,res.size());
  for (i = 0 ; i < res.size() ; ++ i)
  {
    if (i) printf(" ");
    printf("%d" , res[i]);
  }
  printf("\n");
}

int main()
{
    //freopen("~input.txt" , "r" , stdin);
    //int _; scanf("%d\n",&_); while (_--)
    while (~scanf("%d",&n))
        work();
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值