Codeforces 25D

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
", 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
", E[k], E[k + 1]);
    }
    
    return 0;
}
原文地址:https://www.cnblogs.com/Hayasaka/p/14294120.html