1. 程式人生 > 實用技巧 >2020牛客暑期多校訓練營(第二場)C - Cover the Tree - DFS序,構造

2020牛客暑期多校訓練營(第二場)C - Cover the Tree - DFS序,構造

Description

給定一棵無根樹,求它的最小鏈覆蓋,輸出方案。

Solution

以任意非葉子結點定根,如果葉子結點個數為奇數則手工在根上再掛一個

對葉子結點按 DFS 序排序,設有 \(2s\) 個,則 \(1 \to s+1, 2\to s+2,...\),兩兩匹配即可

多掛的那個結點記得在輸出時改成根

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

#define int long long
const int N = 1000005;

int dfn[N],n,m,t1,t2,t3;
vector <int> g[N];
int vis[N];
vector <int> leaf;
int d[N],ind;

void dfs(int p)
{
    vis[p]=1;
    dfn[p]=++ind;
    for(int q:g[p])
    {
        if(!vis[q])
        {
            dfs(q);
        }
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    if(n==1)
    {
        cout<<"1 1"<<endl;
        return 0;
    }
    if(n==2)
    {
        cout<<"1 2"<<endl;
        return 0;
    }
    for(int i=1;i<n;i++)
    {
        cin>>t1>>t2;
        g[t1].push_back(t2);
        g[t2].push_back(t1);
        d[t1]++;
        d[t2]++;
    }
    int sum=0,root;
    for(int i=1;i<=n;i++)
    {
        if(d[i]==1) ++sum, leaf.push_back(i);
        else root=i;
    }
    if(sum&1)
    {
        ++sum;
        leaf.push_back(n+1);
        g[n+1].push_back(root);
        g[root].push_back(n+1);
    }
    dfs(root);
    sort(leaf.begin(),leaf.end(),[](int x,int y){return dfn[x]<dfn[y];});
    cout<<sum/2<<endl;
    for(int i=0;i<sum/2;i++)
    {
        cout<<min(n,leaf[i])<<" "<<min(n,leaf[i+sum/2])<<endl;
    }
}