CF219D

Choosing Capital for Treeland

题面翻译

Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路连接了所有城市。每条道路只能单向通行。现在政府需要决定选择哪个城市为首都。假如城市i成为了首都,那么为了使首都能到达任意一个城市,不得不将一些道路翻转方向,记翻转道路的条数为k。你的任务是找到所有满足k最小的首都。

输入格式

输入包含多个测试点。对于每个测试点,每个测试点的第一行为一个正整数n(\(2<=n<=2·10^{5}\))。接下来 n-1 行,每行两个正整数 \(a_i\)\(b_i\) ,表示城市a到城市b有一条单向通行的道路。输入以空格分隔,以EOF结尾。

输出格式

对于每个测试点,第一行输出k,第二行升序输出所有满足条件的首都的编号。

分析

发现自己写的时候根本没有发现有多组数据,实际上就根本没有多组数据。。。

我们发现,如果选定某一个点作为首都,可以 \(O(n)\) 求出所对应的道路。

但实际上,一共有 \(n\) 个点可以作为首都,如果逐个考虑, \(O(n^2)\) 难以接受!

不妨考虑答案之间的联系。

如果记以 \(u\) 为首都的答案为 res[v]

对于相邻的两点 \(u, v\) ,显而易见的是只有一条边是受到影响的, 即\(\lparen u, v \rparen\)

  • 除去边 \(\lparen u, v\rparen\),剩下的边可以分成两类。
  • 一类距离点 \(u\) 更近的,一类是距离点 \(v\) 更近的。
  • 无论首都是 \(u\) 还是 \(v\) 这些边对答案都没有影响。

那么就可以 \(O(1)\) 地从一个点所对应的答案转移到另一个点。

像这样,一共转移 \(n - 1\) 次,复杂度为 \(O(n)\)

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 5;

using pii = pair<int, int>;

int n, now, ans;

int st[N], top;

vector<pii> g[N];

void dfs_(int u, int fa){
    for (auto [v, ovO] : g[u]){
        if (v == fa) continue;
        now += 1 - ovO; dfs_(v, u);
    }
}

void dfs(int u, int fa, int now){
    if (now == ans) st[++top] = u;
    else if (now < ans) ans = now, top = 0, st[++top] = u;
    for (auto [v, ovO] : g[u]){
        if (v == fa) continue;
        if (ovO) dfs(v, u, now + 1);
        else dfs(v, u, now - 1);
    }
}

signed main(){
    
    cin >> n;
    
    for (int i = 1, u, v; i < n; i++){
        cin >> u >> v;
        g[u].push_back({v, 1}); // 尊度边
        g[v].push_back({u, 0}); // 假度边
    }
    
    dfs_(1, 0);
    dfs(1, 0, ans = now);
    
    cout << ans << endl;
    
    sort(st + 1, st + 1 + top);
    
    for (int i = 1; i <= top; i++)
        cout << st[i] << " ";
    
    return 0;
}

Written with StackEdit中文版.

posted @ 2024-03-29 13:26  afhuds  阅读(2)  评论(0编辑  收藏  举报