# Codeforces Global Round 14_ F. Phoenix and Earthquake(图论构造)

F. Phoenix and Earthquake(图论构造)

题目传送门

题目传送门

题面:

题目大意:

给你n个点,m条边,限制x,每个点都有沥青a[i],定义合并两个点即两点之间有边且a[u]+ a[v] ≥ x,合并之后的沥青为a[u]+ a[v] -x,现在让你输出一种选选边方式,选n − 1条边使得n个点联通。

操作:每次都选最大的a[i],之后将他与跟他相连的且不在他集合中的点合并。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
ll f[maxn];
ll a[maxn];

ll find(ll x) {
    return (x == f[x]) ? x : f[x] = find(f[x]);
    //并查集
}

int main() {
    priority_queue<pair<ll, ll>> pq;
    //优先队列存的是权+编号。
    vector<vector<pair<ll, ll>>> v(maxn);
    //向量存的是v[u]={v,i},(u,v)边是i编号的边。(i=1~m)
    pair<ll, ll> tmp;
    ll n, m, x;
    ll sum = 0;
    cin >> n >> m >> x;
    for (ll i = 1; i <= n; i++) {
        cin >> a[i];
        sum += a[i];
        pq.push({a[i], i});
        f[i] = i;
        //并查集初始化
    }
    for (ll i = 1; i <= m; i++) {
        ll a, b;
        cin >> a >> b;
        v[a].push_back({b, i});
        v[b].push_back({a, i});
    }
    if (1ll * (n * x - x) > sum) {
        printf("No\n");
        //这种情况肯定无解
    } else {
        printf("Yes\n");
        for (ll i = 1; i <= n - 1; i++) {
            tmp = pq.top();
            pq.pop();
            ll t = tmp.second;
            //尽量找a[i]最大的点——tmp。
            //t为点tmp的编号
            while (t != find(t)) {
                tmp = pq.top();
                pq.pop();
                t = tmp.second;
                //如果t已经被别的合并过去了,就跳过一直找。
            }
            ll sn = find(v[t].back().first);
            //sn是下一个合并的点集合的代表点;
            while ((t == sn) && v[t].size())
                //v[t]相连的点和他已经合并过,即该需要连接点所在集合的代表就是t自己,那么就跳过。
            {
                v[t].pop_back();
                sn = find(v[t].back().first);
            }
            ///操作:每次都选最大的a[i],之后将他与跟他相连的且不在他集合中的点合并
            printf("%d\n", v[t].back().second);
            a[t] += a[sn] - x;
            tmp.first = a[t] + a[sn] - x;
            pq.push(tmp);
            f[sn] = t;
            //将sn并入集合

            if (v[t].size() < v[sn].size()) {
                swap(v[t], v[sn]);
                //我们把t视作代表点。
            }

            v[t].insert(v[t].end(), v[sn].begin(), v[sn].end());
            v[sn].clear();
        }
    }
}

遗留问题:

  ///下面这步我是真看不太明白...
          if (v[t].size() < v[sn].size()) {
            swap(v[t], v[sn]);
            //我们把t视作代表点。
        }

因为少这一步,一直WA10…

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值