枚举+求桥 Codeforces701F Break Up

传送门:点击打开链接

题意:最多删掉2条边,使得无向图中s到t不连通。点数<=1e3,边数<=3e4

思路:我们先求一条s到t的任意路径。
我们很容易就能证明,如果存在这样的2条边,或者说只需要删一条边就能让他们两个不连通,那么必然是在s到t的一条路径上。
所以,我们首先求出任意一条s到t的路径。
然后枚举这条路径上的边,最多也只有n-1条。
枚举完后,把这条边删了,求桥,而且这个桥必须要满足,S和T分别在桥的两端。
我们可以这样来判断这种桥是否存在。
首先我们从S开始tarjan,当我们枚举u的一条边e,另一个点是v时候,发现DFN[u]<Low[v],说明这条边是桥,然后我们又发现DFN[v]<=DFN[T],T表示终点,此时就说明,T和v在桥的相同一侧,也就是说T和S就恰好分别在桥的两边了。
找到这样的桥后,再和我们之前枚举删除的边的权值加起来组合更新答案即可。
我通常对于这种方案比较复杂的,写一个Ans的结构体,之后维护方案感觉方便的多

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("input.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 1e3 + 5;
const int INF = 0x3f3f3f3f;

struct Edge {
    int u, v, nxt, cost;
} E[200005];
int n, m, S, T, dfn, del, ans1, ansid;
int Head[MX], erear;
int DFN[MX], Low[MX];
void edge_init() {
    erear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v, int cost) {
    E[erear].u = u;
    E[erear].v = v;
    E[erear].cost = cost;
    E[erear].nxt = Head[u];
    Head[u] = erear++;
}
inline int read() {
    char c = getchar();
    while(!isdigit(c)) c = getchar();

    int x = 0;
    while(isdigit(c)) {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x;
}

class Path {
public:
    bool vis[MX];
    int S[MX], E[MX], fa[MX], sz;
    void solve() {
        sz = 0;
        memset(vis, 0, sizeof(vis));

        queue<int>Q;
        Q.push(::S); vis[::S] = 1;
        fa[::S] = -1;

        bool ok = 0;
        int u, v;
        while(!Q.empty()) {
            u = Q.front(); Q.pop();
            if(u == T) {
                ok = 1; break;
            }
            for(int i = Head[u]; ~i; i =::E[i].nxt) {
                v =::E[i].v;
                if(vis[v]) continue;
                Q.push(v); vis[v] = 1;
                fa[v] = u; E[v] = (i + 2) / 2;
            }
        }
        if(!ok) return;
        u = T;
        while(fa[u] != -1) {
            S[++sz] = E[u];
            u = fa[u];
        }
    }
} path;
struct Ans {
    int id[2];
    int num, totcost;
    Ans() {
        num = 0;
        totcost = 2 * INF;
    }
    void print() {
        if(totcost == 2 * INF) printf("-1\n");
        else {
            printf("%d\n%d\n", totcost, num);
            for(int i = 0; i < num; i++) printf("%d ", id[i]);
        }
    }
};

int P[MX];
int find(int x) {
    return P[x] == x ? x : (P[x] = find(P[x]));
}
void tarjan(int u, int e) {
    Low[u] = DFN[u] = ++dfn;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        if((i + 2) / 2 == del) continue;
        int v = E[i].v;
        if(!DFN[v]) {
            tarjan(v, i | 1);
            Low[u] = min(Low[u], Low[v]);
            if(Low[v] > DFN[u] && DFN[v] <= DFN[T] && ans1 > E[i].cost) {
                ansid = (i + 2) / 2;
                ans1 = E[i].cost;
            }
        } else if((i | 1) != e && DFN[v] < DFN[u]) {
            Low[u] = min(Low[u], DFN[v]);
        }
    }
}
void find_bridge() {
    ans1 = INF;
    dfn = 0;
    memset(DFN, 0, sizeof(DFN));
    tarjan(S, -1);
}
int main() {
    //FIN;
    edge_init();
    scanf("%d%d", &n, &m);
    scanf("%d%d", &S, &T);
    for(int i = 1; i <= n; i++) P[i] = i;
    for(int i = 1; i <= m; i++) {
        int u = read(), v = read(), cost = read();
        edge_add(u, v, cost);
        edge_add(v, u, cost);
        int p1 = find(u), p2 = find(v);
        P[p1] = p2;
    }

    if(find(S) != find(T)) {
        printf("0\n0\n");
        return 0;
    }

    Ans ans;
    path.solve();

    find_bridge();
    if(ans1 != INF) {
        ans.num = 1;
        ans.totcost = ans1;
        ans.id[0] = ansid;
    }

    for(int i = 1; i <= path.sz; i++) {
        del = path.S[i]; //uck(del);
        find_bridge();
        //printf("[%d,%d] %d\n", del, ansid, ans);
        if(ans1 != INF && ans1 + E[(del - 1) * 2].cost < ans.totcost) {
            ans.num = 2;
            ans.id[0] = ansid; ans.id[1] = del;
            ans.totcost = ans1 + E[(del - 1) * 2].cost;
        }
    }
    ans.print();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值