Codeforces gym 101675B Cactusophobia

题面:2016 Russian Code Cup (RCC 16), Final Round B.Cactusophobia

题意:给定一棵边仙人掌(每条边最多在一个环内),每条边有一种颜色。现要删除一些边,使得边仙人掌变成一棵树,且剩下边的颜色种类最多。求最大的剩下边的颜色种类。

题解点双连通分量 + 最大流

求边仙人掌的点双连通分量。对于每个点双连通分量内边的数量,如大于 1 1 1,则说明是一个环;否则为一个点及一条边。

对于大小大于 1 1 1的点双连通分量, 需删去一条边;否则保留此边。

将源点与每个点双连通分量连边,如大小大于1,则流量为 s i z e − 1 size - 1 size1;否则流量为 1 1 1

将每个点双连通分量与分量内边所对的颜色连边,流量为 1 1 1

每种颜色与汇点连边,流量为 1 1 1

最大流即可。

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef pair<int, int> P ;
const int maxn = 4e4 + 10, INF = 0x3f3f3f3f ;
struct edge {
    int to, cap, rev ;
    edge () {}
    edge (int _to, int _cap, int _rev) {to = _to, cap = _cap, rev = _rev;}
} ;
vector<edge> G[maxn] ;
int level[maxn], iter[maxn] ;
void addEdge (int from, int to, int cap) {
    //printf("%d %d %d\n", from, to, cap) ;
    G[from].push_back (edge (to, cap, G[to].size())) ;
    G[to].push_back (edge (from, 0, G[from].size() - 1)) ;
}
void bfs (int s) {
    memset (level, -1, sizeof level) ;
    queue<int> que ;
    que.push (s); level[s] = 0 ;
    while (!que.empty()) {
        int v = que.front(); que.pop() ;
        for (int i = 0; i < G[v].size(); i ++) {
            edge e = G[v][i] ;
            if (e.cap > 0 && level[e.to] < 0) {
                level[e.to] = level[v] + 1 ;
                que.push (e.to) ;
            }
        }
    }
}
int dfs (int v, int t, int f) {
    if (v == t) return f ;
    for (int &i = iter[v]; i < G[v].size(); i ++) {
        edge &e = G[v][i] ;
        if (e.cap > 0 && level[e.to] > level[v]) {
            int d = dfs (e.to, t, min (f, e.cap)) ;
            if (d > 0) {
                e.cap -= d ;
                G[e.to][e.rev].cap += d ;
                return d ;
            }
        }
    }
    return 0 ;
}
int maxflow (int s, int t) {
    int flow = 0 ;
    for (;;) {
        bfs (s) ;
        if (level[t] < 0) return flow ;
        memset (iter, 0, sizeof iter) ;
        int f ;
        while ((f = dfs (s, t, INF)) > 0) flow += f ;
    }
}
int n, m ;
vector<P> g[maxn] ;
int dfn[maxn], low[maxn], stk[maxn], num, top, cnt ;
vector<int> dcc[maxn] ;
int color[maxn] ;
void add_Edge (int u, int v, int id) {
    g[u].push_back (P (v, id)) ;
    g[v].push_back (P (u, id)) ;
}
void tarjan (int v, int fa) {
    dfn[v] = low[v] = ++ num ;
    for (int i = 0; i < g[v].size(); i ++) {
        int y = g[v][i].fi, id = g[v][i].se ;
        if (y == fa) continue ;
        if (!dfn[y]) {
            stk[++ top] = id ;
            tarjan (y, v) ;
            low[v] = min (low[v], low[y]) ;
            if (low[y] >= dfn[v]) {
                cnt ++ ;
                while (stk[top] != id) dcc[cnt].push_back (stk[top --]) ;
                dcc[cnt].push_back (stk[top --]) ;
            }
        } else if (dfn[y] < dfn[v]) {
            stk[++ top] = id ;
            low[v] = min (low[v], dfn[y]) ;
        }
    }
}
int main() {
    cin >> n >> m ;
    for (int i = 1; i <= m; i ++) {
        int u, v, c ;
        scanf("%d%d%d", &u, &v, &c) ;
        color[i] = c ;
        add_Edge (u, v, i) ;
    }
    tarjan (1, 0) ;
    int s = 0, t = cnt + m + 1 ;
    for (int i = 1; i <= cnt; i ++) {
        if (dcc[i].size() == 1) addEdge (s, i, 1) ;
        else addEdge (s, i, dcc[i].size() - 1) ;
        for (int j = 0; j < dcc[i].size(); j ++)
            addEdge (i, cnt + color[dcc[i][j]], 1) ;
    }
    for (int i = 1; i <= m; i ++)
        addEdge (cnt + i, t, 1) ;
    printf("%d\n", maxflow (s, t)) ;
    return 0 ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值