Luogu - CF638C 【Road Improvement】 - 題解
阿新 • • 發佈:2020-08-23
這是一道貪心題。
- 求出 \(k\) 其實很簡單,因為每個節點的邊每天最多隻能修一條,所以答案就是度數最大的節點的度數(也就是邊最多的節點的邊數)。
- 重點在於怎麼求方案。
- 因為題目中的樹是一顆無根樹,所以我們預設 \(1\) 號節點為他的根節點。
- 接下來我們開 \(k\) 個集合,表示第 \(i\) 天要修的路的編號(這裡可以用 vector 實現)。
劃重點然後開始從 \(1\) 號節點 dfs ,每走到一個節點,就把 它與它父親之間的路 與 它與所有兒子間的路 放在不同的集合中,因為這些邊的修築 必須都有該節點參與 ,但是由於該節點 一天只能修一條路 ,所以要放在不同天修。- 就這樣遍歷完整棵樹,然後按要求輸出就可以了。
Code:
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<bitset> #include<cmath> #include<queue> #include<map> #include<set> using namespace std; int read() { int ans=0,f=1; char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return ans*f; } const int N=2e5+5; int n,du[N],head[N],tot,ans; //du 用來統計節點度數 vector<int> agg[N]; //用 vector 模擬集合 struct Edge { int v,ne; }e[N*2]; //這裡要開雙倍空間,因為是雙向邊 inline void add(int u,int v); //加邊函式 inline void dfs(int u,int fa,int last); //dfs 求方案 int main() { n=read(); for(int i=1;i<n;++i) { int u=read(),v=read(); du[u]++,du[v]++;//統計度數 add(u,v); add(v,u); ans=max(ans,max(du[u],du[v])); //取度數最大值為答案 } printf("%d\n",ans); //先輸出答案 dfs(1,-1,0); //然後 dfs 求方案 for(int i=1;i<=ans;++i) { printf("%d ",agg[i].size()); //先輸出這一天修的路的數量 for(int j=0;j<agg[i].size();++j) printf("%d ",agg[i][j]); //然後輸出具體的路的編號 printf("\n"); } return 0; } inline void add(int u,int v) { e[++tot]=(Edge){v,head[u]}; head[u]=tot; } //u 表示當前節點,fa 表示 u 節點的父親,last 表示 u 與 fa 間的路所在的集合編號 inline void dfs(int u,int fa,int last) { int j=1; // j 表示當前列舉到的集合數 for(int i=head[u];i;i=e[i].ne) { int v=e[i].v; if(v==fa) //判斷一下,別走回頭路 continue; if(j==last) //如果當前集合編號是 u 與它父親間的路所在集合的編號 ++j; //那麼這個集合不能放這條邊,及 ++j; agg[j].push_back((i+1)/2); //然後記錄答案 //因為上面存的是雙向邊,所以不能直接存 i //考慮到同一條邊在陣列中的下表是 x,x+1 //所以通過 (x+1)/2 可以計算出該邊在讀入時的編號。 dfs(v,u,j); //繼續遞迴該節點 ++j; //因為該集合已經放了邊,所以 j 要 +1 } return; }