校内欢乐赛之爆零季 POJ 1904

题意:Ri爷的题,写了50分暴力,,,然后想搞退流无果,,被Cena咖啡了,,就不多说啥了吧
过了大数据,,,貌似跑的很慢,,,是不是常数太大?
知识点:tarjan强连通分量,二分图匹配
idea很好,,,由增广路推倒出做法,将匹配边反向形成强连通分量是关键
相关:LA 2966 HDU 4685(加强版)
未完待续

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define N 2000
using namespace std;
struct edge{ int nxt,point,v;};
edge e[((N*N)<<1)+5];
int tot[(N<<1)+5],n,a[N+5][N+5],link[(N<<1)+5],time,dfn[(N<<1)+5],low[(N<<1)+5],
stack[(N<<1)+5],num,color[(N<<1)+5],cnt;
bool vis[(N<<1)+5],in[(N<<1)+5];
inline void addedge(int u1,int v1){
    e[++cnt].nxt = e[u1].point; e[u1].point = cnt; e[cnt].v = v1;}
inline void insert(int u1,int v1){ addedge(u1,v1); addedge(v1,u1);}
inline int getnum(){
    char c; int num;
    while (!isdigit(c = getchar()));
    num = c - '0';
    while (isdigit(c = getchar())) num = 10*num +c -'0';
    return num;
}
void init(){
    n = getnum(); cnt = 0;
    for (int i = 1;i <= n; ++i){
        a[i][0] = getnum();
        for (int j = 1;j <= a[i][0]; ++j) a[i][j] = getnum(),insert(i,a[i][j]+n);
        sort(a[i]+1,a[i]+a[i][0]+1);
    }
}

inline bool dfs1(int x){
    for (int p = e[x].point; p ;p = e[p].nxt)
    {
        int t = e[p].v;
        if (!vis[t]){
        vis[t] = 1;
        if (!link[t]||dfs1(link[t])){
            link[t] = x;
            return 1;}
        }
    }
    return 0;
}

void match_it(){
    memset(link,0,sizeof(link));
    for (int i = 1;i <= n+n; ++i){
        memset(vis,0,sizeof(vis));
        dfs1(i);
    }
}

void make_it(){
    memset(e,0,sizeof(e));
    for (int i = 1;i <= n; ++i)
      for (int j = 1;j <= a[i][0]; ++j){
        if (link[a[i][j]+n]==i) addedge(a[i][j]+n,i);
        addedge(i,a[i][j]+n);}
}

inline void pop_it(int x){
    while (stack[num] != x) in[stack[num]] = 0, color[stack[num--]] = x;
    in[x] = 0; num--;
}

inline void dfs2(int x){
    dfn[x] = low[x] = ++time;
    stack[++num] = x; in[x] = 1;
    for (int p = e[x].point;p ; p = e[p].nxt)
        if (!vis[e[p].v]) {
          vis[e[p].v] = 1;
          dfs2(e[p].v);
          low[x] = min(low[x],low[e[p].v]);}
        else if (in[e[p].v]) low[x] = min(low[x],dfn[e[p].v]);
    if (dfn[x] == low[x]) pop_it(x);
}

void DO_IT(){
    match_it();
    make_it();
    time = 0; num = 0;
    memset(vis,0,sizeof(vis));
    for (int i = 1;i <= n+n; ++i) color[i] = i;
    for (int i = 1;i <= n+n; ++i)
      if (!vis[i]) dfs2(i);
}

void print(){
    memset(tot,0,sizeof(tot));
    for (int i = 1;i <= n; ++i)
      for (int j = 1;j <= a[i][0]; ++j)
        if (color[i]==color[a[i][j]+n]) tot[i] ++;
    for (int i = 1;i <= n; ++i){
      printf("%d ",tot[i]);
      for (int j = 1;j <= a[i][0]; ++j)
        if (color[i]==color[a[i][j]+n]) printf("%d ",a[i][j]);
      puts("");}
}

int main(){
    freopen("quest.in","r",stdin);
    freopen("quest.out","w",stdout);
    init();
    DO_IT();
    print();
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值