CF1108f 并查集

博客详细探讨了如何利用并查集解决竞赛CF1108f中的冲突处理问题,针对每种情况w进行分析,强调了在遇到同层冲突时的处理策略。

每种w一起考虑,与同层冲突就加

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>

using namespace std;

int n,m;
struct node
{
    int u,v,w;
    friend bool operator<(node a,node b)
    {
        return a.w>b.w;
    }
}itm[200005];

int fa[200005];

int gf(int x)
{
    if(x!=fa[x])
        return fa[x]=gf(fa[x]);
    return fa[x];
}

int main() {

    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;i++)
            fa[i]=i;

        priority_queue<node> q;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&itm[i].u,&itm[i].v,&itm[i].w);
            q.push(itm[i]);
        }

        int cnt=0,ans=0;
        while(!q.empty() && cnt<(n-1))
        {
            node sta=q.top();
            q.pop();

            queue<node>q1,q2;
            q1.push(sta);
            while(!q.empty() && q.top().w==sta.w)
            {
                q1.push(q.top());
                q.pop();
            }

            while(!q1.empty())
            {
                node cur=q1.front();
                q1.pop();

                int f1=gf(cur.u);
                int f2=gf(cur.v);
                if(f1!=f2)
                    q2.push(cur);
            }

            if(!q2.empty())
            {
                node cur=q2.front();
                q2.pop();

                int f1=gf(cur.u);
                int f2=gf(cur.v);
                fa[f1]=f2;
                cnt++;

                while(!q2.empty())
                {
                    node tp=q2.front();
                    q2.pop();
                    f1=gf(tp.u);f2=gf(tp.v);
                    if(f1==f2)
                        ans++;
                    else
                        fa[f1]=f2,cnt++;
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

### 关于洛谷 CF1907F 的题目翻译 #### 题目描述 给定一棵树,每条边有一个权值 $ w_i $。定义路径的 **价值** 为该路径上的最大边权减去最小边权。对于所有的简单路径,求它们的价值之和。 输入数据包含多个测试用例。每个测试用例的第一行为两个整数 $ n $ 和 $ m $ ($ 2 \leq n \leq 10^5, 1 \leq m \leq 10^5 $),分别表示树中的节点数量和询问次数。接下来 $ n-1 $ 行,每行三个整数 $ u, v, w $ ($ 1 \leq u, v \leq n, 1 \leq w \leq 10^9 $),表示一条连接节点 $ u $ 和 $ v $ 权值为 $ w $ 的无向边。随后的一行包含一个整数 $ m $,之后 $ m $ 行每一行有两个整数 $ a, b $ ($ 1 \leq a, b \leq n $),表示查询从节点 $ a $ 到节点 $ b $ 的路径价值总和。 输出共 $ m $ 行,第 $ i $ 行对应第 $ i $ 个查询的结果。 --- #### 解法概述 此问题可以通过离线处理的方式解决。核心思路是对所有边按照权重排序,并利用并查集维护连通分量的信息。通过动态更新连通分量的最大/最小边权,可以高效地计算任意两点间路径的价值总和[^1]。 以下是基于 C++ 的代码实现: ```cpp #include <bits/stdc++.h> using namespace std; struct Edge { int u, v; long long w; }; // 并查集结构体 struct DSU { vector<int> parent; vector<long long> minW, maxW; DSU(int n) : parent(n + 1), minW(n + 1, LLONG_MAX), maxW(n + 1, 0) { for (int i = 0; i <= n; ++i) parent[i] = i; } int find_set(int x) { return parent[x] != x ? parent[x] = find_set(parent[x]) : x; } void union_sets(int x, int y, long long w) { int fx = find_set(x); int fy = find_set(y); if (fx == fy) return; parent[fy] = fx; minW[fx] = min({minW[fx], minW[fy], w}); maxW[fx] = max({maxW[fx], maxW[fy], w}); } }; const int MAXN = 1e5 + 5; vector<pair<int, int>> queries[MAXN]; long long answer[MAXN]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<Edge> edges(n - 1); for (auto &[u, v, w] : edges) { cin >> u >> v >> w; } // 处理查询 for (int i = 0; i < m; ++i) { int a, b; cin >> a >> b; queries[a].emplace_back(b, i); queries[b].emplace_back(a, i); } sort(edges.begin(), edges.end(), [&](const Edge &a, const Edge &b) -> bool { return a.w < b.w; }); DSU dsu(n); for (const auto &[u, v, w] : edges) { dsu.union_sets(u, v, w); for (const auto &[node, idx] : queries[u]) { if (dsu.find_set(node) == dsu.find_set(u)) { answer[idx] += dsu.maxW[dsu.find_set(u)] - dsu.minW[dsu.find_set(u)]; } } for (const auto &[node, idx] : queries[v]) { if (dsu.find_set(node) == dsu.find_set(v)) { answer[idx] += dsu.maxW[dsu.find_set(v)] - dsu.minW[dsu.find_set(v)]; } } } for (int i = 0; i < m; ++i) { cout << answer[i] << "\n"; } } ``` --- ####
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值