hdu 6763 2020杭电多校 第二场 1001

Total Eclipse

http://acm.hdu.edu.cn/showproblem.php?pid=6763

题意

这题的题意非常讨厌,我喜欢转化成我的说法:

有一个图模型的城市,建筑之间可能有双向边(可能重边),每个建筑有高度。

每过一个单位时间,海水淹没一个高度,建筑被完全淹没就失去边。

让你对“每个时刻的建筑群(连通块)个数”求和,直到所有建筑被淹没。

思路

这题就需要细心地考虑一个并查集的过程。(只要读对了题意,我们队可以提供三种不同的解法,气死👿)

  1. 观察到整个过程反过来不影响答案,所以我从反向考虑这个问题。

  2. 随着淹没高度增加,显然会把高的建筑孤立,甚至有可能最高的建筑要被单独计算多次。

  3. 从最高的建筑开始:

    1. 如果当前的建筑相邻建筑有比他的建筑群(连通块),并且当前这个建筑还不属于这个建筑群。
    2. 那么把当前建筑划入建筑群,并且,这个建筑群的贡献就是建筑群的高度与当前建筑的差值。
    3. 要注意,这个建筑划入这个建筑群之后,整个建筑群的有效高度,要变成新来建筑的有效高度。
  4. 最终格局就等价于对原始图做并查集,并且每个建筑群的有效高度都是这个块的最小高度。

  5. 把最后每个连通块的有效高度都加入到答案即可。

代码

代码里有一些细节:

按照上面的思路其实是要写带权并查集的,因为你要维护并查集的有效高度。

但其实只要你巧妙的使用合并操作,就可以实现同样的效果。

复杂度是并查集的复杂度+vector建图的复杂度。运行时间慢于第一页的人,可能是我用cin的缘故。

int fa[MAXN];

int getf(int x) {
    if (x == fa[x]) return x;
    else return fa[x] = getf(fa[x]);
}
// 注意,我是把右边的祖先合并给左边的祖先。
bool unif(int x, int y) {
    int fx = getf(x);
    int fy = getf(y);
    if (fx != fy) {
        fa[fy] = fx;
        return true;
    }
    return false;
}

vector<int> g[MAXN];
int val[MAXN];
int arr[MAXN];
//int vis[MAXN];

void solve(int kaseId = -1) {
    int n, m;
    ll ans = 0;
    cin >> n >> m;

    for (int i = 1; i <= n; ++i) {
        cin >> val[i];
        fa[i] = i;
        g[i].clear();
        arr[i] = i;
    }

    for (int i = 1, u, v; i <= m; ++i) {
        cin >> u >> v;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
    }

    sort(arr + 1, arr + 1 + n, [&](int x, int y) {
        return val[x] > val[y];
    });

    for (int i = 1; i <= n; ++i) {
        int u = arr[i];
        for (auto v : g[u]) {
            if (val[v] >= val[u]) {
                if (unif(v, u)) {
                    v = getf(v);
                    ans += val[v] - val[u];
                    val[v] = val[u];
                }
            }
        }
    }

    for (int i = 1; i <= n; ++i) {
        int u = getf(i);
        ans += val[u];
        val[u] = 0;
    }

    cout << ans << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值