Fools and Foolproof Roads(并查集+优先队列)

本文介绍了一种使用并查集和优先队列解决特定图论问题的方法,通过构建不同区域间的连接,确保最终达到指定的区域数量。讨论了如何处理不同区域和同一区域内的边权值,以及如何避免自环边的创建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:传送门

题意描述:

       给你n个城市,m条边,要你建设p条边,使得最后的区域数为q(区域就是块)。如果为不同区域,建边的权值为min(1e9, 两个区域的变数权值加一)。同一区域,权值为1000。问你是否能执行上述操作,使得最终区域数位q。

题意分析:

       因为要用到块,所以先用并查集或强联通分量求出有多少个块,然后判断块的数目t是否小于q,如果小于q,则直接输出NO;否则,就用优先队列建设建设t-q条不同区域的边,因为同一区域内建边权值为1000,而且可以重边,所以是先建设不同区域的边。

然后再建设剩下的边,要注意的是,边不能自环。所以这题就是用并查集+优先队列就可以解决了,还要注意一些细节。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, int>P;
const ll inf = 1e9;
int n, m, p, q;
int fa[100005];
struct road{
    ll from, to, value;
}ro[100005];

int found(int x){
    return x == fa[x]? x : fa[x] = found(fa[x]);
}
void unite(int x, int y){
    x = found(x), y = found(y);
    fa[x] = y;
}
vector<int>Q[100005];
vector<int>vis;
priority_queue<P, vector<P>, greater<P> >pri;
ll num[100005];
int main(){
    cin >> n >> m >> p >> q;
    for(int i=0;i<=n;++i) fa[i] = i;
    for(int i=0;i<m;++i){
        scanf("%d %d %d", &ro[i].from, &ro[i].to, &ro[i].value);
        unite(ro[i].from, ro[i].to);
        Q[ro[i].from].push_back(ro[i].to);
        Q[ro[i].to].push_back(ro[i].from);
    }
    for(int i=0;i<m;++i){
        int temp = found(ro[i].from);
 //       if(temp == ro[i].from && !num[temp])
        if(!num[temp]) vis.push_back(temp);
        num[temp] += ro[i].value;
    }
    for(int i=1;i<=n;++i){
        int temp = found(i);
        if(!num[temp]) vis.push_back(temp);
    }
 //   cout << vis.size() << endl;
    for(int i=0;i<vis.size();++i){
        int v = vis[i];
        ll d = min(inf, num[v]);
        pri.push({d, v});
    }
    if(vis.size() < q) cout << "NO" << endl;
    else if(vis.size() == q){
        if(m == 0 && p) cout << "NO" << endl;
        else{
            cout << "YES" << endl;
            P v = pri.top();
            pri.pop();
            while(!v.first && !pri.empty()){
                v = pri.top();
                pri.pop();
            }
            for(int i=0;i<p;++i) printf("%d %d\n", v.second, Q[v.second][0]);
        }
    }
    else{
        int d = vis.size()-q;
        if(d > p) cout << "NO" << endl;
        else{
            cout << "YES" << endl;
            for(int i=0;i<d;++i){
                P u = pri.top(); pri.pop();
                P v = pri.top(); pri.pop();
                printf("%d %d\n", u.second, v.second);
       //         printf("%lld %lld\n", u.first, v.first);
                Q[u.second].push_back(v.second);
                ll dd = min(inf, (ll)(2ll*(u.first+v.first)+1));
        //        cout << dd << endl;
                pri.push({dd, u.second});
            }
            P v = pri.top();
            pri.pop();
            while(!v.first && !pri.empty()){
                v = pri.top();
                pri.pop();
            }
            for(int i=d;i<p;++i){
                printf("%d %d\n", v.second, Q[v.second][0]);
            }
        }
    }
    return 0;
}

 

### 关于2025年愚人节技术笑话或IT相关事件的信息 目前关于具体日期如2025年的愚人节(April Fools' Day 2025)上的科技笑话或IT相关活动尚未有确切记录。然而,可以推测的是,在这一天可能会延续以往的传统,各大科技公司和媒体会发布一些具有创意和技术背景的恶作剧内容[^1]。 通常情况下,每年的愚人节期间,许多知名的技术企业都会利用这一机会推出虚构的产品或者服务来娱乐公众。例如谷歌曾宣布过不存在的功能如“Google Nose”用于嗅觉搜索;又比如特斯拉可能开玩笑说他们开发了一款可以在火星上种植植物的家庭设备等等。这些玩笑往往基于当前快速发展的科学技术趋势之上,并结合幽默感吸引人们的注意[^2]。 尽管现在还没有针对2025年具体的计划被公开披露出来,但随着人工智能、量子计算以及其他前沿领域研究不断取得进展,未来几年里我们可以期待更加复杂且令人惊叹的 April Fools’ pranks from tech giants. 对于个人而言,也可以尝试自己动手制作简单的程序作为朋友间的玩笑材料。下面提供了一个简单Python脚本例子,它假装能够预测用户的幸运号码: ```python import random def get_lucky_number(name): lucky_number = random.randint(1, 100) return f"{name}, your predicted lucky number is {lucky_number}!" print(get_lucky_number(input("Enter your name to predict your lucky number: "))) ``` 此代码接受输入的名字并随机生成一个介于1到100之间的整数作为所谓的“幸运号”。虽然这只是个小把戏,但在特定场合下却能增添不少乐趣。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值