codeforces 723F 并查集+贪心

先去掉\(s\)\(t\)做一个最小生成森林并缩点,然后把缩后的点分成三类:
  - I. 不与\(s\)\(t\)中任意一个点相连。若存在这类点则无解;
  - II. 仅与\(s\)\(t\)中的一个点相连。肯定要连上。
  - III. 与\(s\)\(t\)都相连。在II型点都处理完后再处理III型点。贪心一下,每次取\(s\)\(t\)中所剩的度数较多那一个连之即可;如果到过程中发现度数不够,则无解。
  但要注意一个细节!如果\(s\)\(t\)在原图中就是相连的,必须连这条边的充要条件是:所有缩后的点都是II型点。因为我们要保证\(s\)\(t\)相连,所以有两种选择:一是把一个III型点的两条边都连上(如果有III型点);二是连\(s\)\(t\)的边(如果有)。显然,如果存在III型点,还连s-t边的方案不会更优。所以,只有当迫不得已(全都是II型点)的时候,才连s-t边救场子。反过来,如果全是II型点却没有s-t边,是无解的。
  用一堆数组记录方案。
  代码如下:

#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;

 #define rep(i,a,b) for (int i = a; i <= b; i++) 
 #define dep(i,a,b) for (int i = a; i >= b; i--)
 #define vep(i,v) for (int i = 0; i < (int)v.size(); i++)
 #define read(x) scanf("%d", &x)
 #define fill(a,x) memset(a, x, sizeof(a))
 #define mp make_pair
 #define pb push_back

 const int N = 200000 + 5, M = 400000 + 5;

 int n, m, u[M], v[M], s, t, ds, dt, cnt = 0, id[N], from[N][2], fa[N];
 bool con[N][2], link[N][2], need_direct = true;
 vector<pair<int, int> > ans;

 int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

 void init() {
    fill(con, false);
    fill(link, false);
    fill(id, 0);
 }

 bool is_st(int x) { return (x == s || x == t); }

int main()
{
    read(n); read(m);
    rep(i,1,m) read(u[i]), read(v[i]);
    read(s); read(t); read(ds); read(dt);

    init();
    rep(i,1,m) {
        if (u[i] == s) con[v[i]][0] = true;
        if (u[i] == t) con[v[i]][1] = true;
        if (v[i] == s) con[u[i]][0] = true;
        if (v[i] == t) con[u[i]][1] = true;
    }

    rep(i,1,n) fa[i] = i;
    rep(i,1,m) {
        int x = u[i], y = v[i];
        if (is_st(x) || is_st(y)) continue;
        int f1 = find(x), f2 = find(y);
        if (f1 != f2) { fa[f1] = f2; ans.pb(mp(x, y)); }
    }

    rep(i,1,n) {
        if (is_st(i)) continue;
        int f = find(i);
        if (!id[f]) { cnt++; id[f] = cnt; }
        int j = id[f];
        if (con[i][0]) { link[j][0] = true; from[j][0] = i; }
        if (con[i][1]) { link[j][1] = true; from[j][1] = i; }
    }

    rep(i,1,cnt) 
        if (link[i][0] ^ link[i][1]) {
            if (link[i][0]) { ds--; ans.pb(mp(from[i][0], s)); }
            if (link[i][1]) { dt--; ans.pb(mp(from[i][1], t)); }
        }
        else {
            need_direct = false;
            if ((link[i][0] | link[i][1]) == 0) { printf("No\n"); return 0; }
        }

    bool st_have_linked = false;
    if (need_direct) {
        rep(i,1,m) 
            if (is_st(u[i]) && is_st(v[i])) { 
                dt--; ds--;
                ans.pb(mp(t, s));
                need_direct = false;
                break;
            }
            if (need_direct) { printf("No\n"); return 0; }
    }
    else 
        rep(i,1,cnt) if (link[i][0] & link[i][1]) {
            if (!st_have_linked) { 
                ds--; ans.pb(mp(from[i][0], s)); 
                dt--; ans.pb(mp(from[i][1], t));
                st_have_linked = true;
                continue;
            }
            if (ds >= dt) 
                { ds--; ans.pb(mp(from[i][0], s)); }
            else
                { dt--; ans.pb(mp(from[i][1], t)); }
            if (ds < 0 || dt < 0) { printf("No\n"); return 0; }
    }
    
    if (ds < 0 || dt < 0) { printf("No\n"); return 0; }
    printf("Yes\n");
    vep(i,ans) printf("%d %d\n", ans[i].first, ans[i].second);

    return 0;
}

转载于:https://www.cnblogs.com/yearwhk/p/5932055.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值