Codeforces 25D - Roads not only in Berland(并查集判环)

D. Roads not only in Berland

time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output

Berland Government decided to improve relations with neighboring countries. First of all, it was decided to build new roads so that from each city of Berland and neighboring countries it became possible to reach all the others. There are n cities in Berland and neighboring countries in total and exactly n - 1 two-way roads. Because of the recent financial crisis, the Berland Government is strongly pressed for money, so to build a new road it has to close some of the existing ones. Every day it is possible to close one existing road and immediately build a new one. Your task is to determine how many days would be needed to rebuild roads so that from each city it became possible to reach all the others, and to draw a plan of closure of old roads and building of new ones.

Input

The first line contains integer n (2 ≤ n ≤ 1000) — amount of cities in Berland and neighboring countries. Next n - 1 lines contain the description of roads. Each road is described by two space-separated integers ai, bi (1 ≤ ai, bi ≤ n, ai ≠ bi) — pair of cities, which the road connects. It can’t be more than one road between a pair of cities. No road connects the city with itself.

Output

Output the answer, number t — what is the least amount of days needed to rebuild roads so that from each city it became possible to reach all the others. Then output t lines — the plan of closure of old roads and building of new ones. Each line should describe one day in the format i j u v — it means that road between cities i and j became closed and a new road between cities u and v is built. Cities are numbered from 1. If the answer is not unique, output any.

Examples
input
2
1 2
output
0
input
7
1 2
2 3
3 1
4 5
5 6
6 7
output
1
3 1 3 7

题目大意:

你会得到一个图,第一行输入一个n,表示有n个顶点,接下来输入n - 1行,每行输入两个数a, b 表示a -> b 有一条边,你的任务是更改已有的边,使图成为连通图,第一行输出一个整数S,表示操作的最少次数,接下来S行每行四个整数u1,v1,u2,v2,表示将原来连接u1,v1的边移动到u2,v2之间。

解题思路:

纪念一下, 为数不多的靠自己独立思考A掉1900的题之一。刚开始用拓扑排序和dfs搞了一大会,结果发现方向错了,后来想了下,可以抽象成连通块问题,因为这道题有n - 1条边,如果连通的话一定没有环,如果出现环的话就说明这个边是多余的了。然后我们可以首先输出连通块数量 - 1,再通过并查集判环,找到出问题的边,记录一下,最后把连通块连起来就好了。
判环:每次输入的a, b 我们都尝试把b 并到 a点上,如果有环则记录一下出问题的边即可。

Code:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) x &(-x)

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 1e3 + 50;

int n;
int f[N];
vector<pii > v;
vector<int > E;

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

bool merge(int x, int y) {
    int t1 = find(x);
    int t2 = find(y);
    if (t1 != t2) {
        f[t2] = t1;
        return true;
    }
    return false;
}

int main()
{
    scanf("%d", &n);
    
    for (int i = 1; i <= n; i ++) f[i] = i;
    for (int i = 1; i < n; i ++) {
        int a, b;
        scanf("%d%d", &a, &b);
        if (!merge(a, b)) v.push_back({a, b});//如果没法合并则说明有环,记录一下问题边
    }

    int res = 0;
    for (int i = 1; i <= n; i ++)
        if (f[i] == i) {//每个连通块的夫节点记录一下,最后父节点依次连接即可
            res ++;
            E.push_back(i);
        }
    printf("%d\n", res - 1);//连通块的数量 - 1即为操作数
    
    for (int i = 0, k = 0; i < v.size(); i ++, k ++) {
        printf("%d %d ", v[i].first, v[i].second);
        printf("%d %d\n", E[k], E[k + 1]);
    }
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值