【cf527 E】Minimal Diameter Forest


題意

給你一個森林(若干棵樹),向其中加邊,使得最后形成一棵樹,要求最后形成的樹的直徑最小,輸出這棵樹的直徑和所加的邊

分析

每加一條邊,都可以將兩棵樹合並成一棵樹,總共有t = n - m棵樹

也就是說我們需要在這t棵樹里面,分別選一個點,將他們連接起來,使得最后形成一棵直徑最小的樹

這個點的性質是,以這個點為根節點轉化為有根樹的畫,樹的高度一定是最小的(與重心類似,最長鏈最短可以保證合並后直徑最短)

稍加分析可以知道,這個點一定是直徑的中點(直徑兩端到該點的距離相差不超過1)

所以我們維護一棵樹,可以維護以下幾點性質:

1. 樹的直徑dig

2. 樹的直徑的中點序號center

3. 樹的直徑兩端離center較遠的點(相同則任意)site

這樣合並A,B兩顆樹后,新的樹

dig = max ( A.dig , B.dig , ceil(A.dig/2) + ceil(B.dig/2) )

遍歷這棵樹,仍然求得新的center和site

那么以怎樣的順序合並呢?

通過上面dig的轉移式可以看出,如果兩棵樹site到center的距離不相等,則形成的新樹的直徑不變(等於大的一棵樹),否則直徑變大

所以我們希望最小化這個影響(不變大)

考慮最大的三棵樹$A.dig ≥ B.dig ≥ C.dig$

第一次將最大的兩顆樹合並起來,得到的一棵新樹D

如果$A.dig == B.dig$

則$D.dig > C.dig$則之后D和C合並直徑不會變

否則$A.dig > C.dig$之后D和C合並直徑不會變

可證得每次合並最大的兩棵樹

如此操作下去直到合成一棵樹,得到的直徑一定最小

其實不用優先隊列維護,只需要排序后,每次取未合並的最大的樹,與當前維護的樹合並即可(類似prim算法合並的思路)

代碼

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 int n,m;
  5 vector<int>edge[1005];
  6 int color[1005];
  7 void rs(int pre,int now,int co) {
  8     color[now] = co;
  9     for(auto it = edge[now].begin();it!=edge[now].end();it++) {
 10     if(*it == pre) continue;
 11     rs(now,*it,co);
 12     }
 13     return;
 14 }
 15 struct Tree{
 16     int dig,center,site;
 17     bool operator < (const Tree& o) const {
 18     return dig < o.dig;
 19     }
 20 }tree;
 21 priority_queue<Tree>pq;
 22 int far,X,Y,id;
 23 void dfs(int pre,int now,int dis){
 24     if(dis>far) {
 25     far = dis;id =now;
 26     }
 27     for(auto it = edge[now].begin();it!=edge[now].end();it++) {
 28     if(*it == pre) continue;
 29     dfs(now,*it,dis+1);
 30     }
 31 }
 32 stack<int>st;
 33 int flag;
 34 vector<pair<int,int> >ans;
 35 void dfs2(int pre,int now,int aim){
 36     if(now == aim) {
 37     tree.site = now;
 38     for(int i=1;i<(far+1)/2;i++){ st.pop();}
 39     tree.center = st.top();
 40     tree.dig = far;
 41     pq.push(tree);
 42     flag = 1;
 43     while(!st.empty())st.pop();
 44     return;
 45     }
 46     st.push(now);
 47     for(auto it = edge[now].begin();it!=edge[now].end();it++) {
 48     if(*it == pre) continue;
 49     dfs2(now,*it,aim);
 50     if(flag) return;
 51     }
 52     st.pop();
 53 }
 54 int main() {
 55     ios::sync_with_stdio(false);
 56     cin>>n>>m;
 57     int x,y;
 58     for(int i=1;i<=m;i++) {
 59     cin>>x>>y;
 60     edge[x].push_back(y);
 61     edge[y].push_back(x);
 62     }
 63     int co = 0;
 64     for(int i=1;i<=n;i++) {
 65     if(color[i]==0) {
 66         rs(0,i,++co);
 67         far=0;id = i;
 68         dfs(0,i,0);
 69         X = id;
 70         far=0;id = X;
 71         dfs(0,X,0);
 72         Y = id;
 73         if(X == Y) {
 74         tree.site = X;
 75         tree.center = X;
 76         tree.dig = 0;
 77         pq.push(tree);
 78         }
 79         else {
 80         flag =0;
 81         dfs2(0,X,Y);
 82         }
 83     }
 84     }
 85     while(pq.size()>1) {
 86     Tree a = pq.top();pq.pop();
 87     Tree b = pq.top();pq.pop();
 88     far = max(a.dig,((a.dig+1)/2) + ((b.dig+1)/2) +1);
 89     far = max(b.dig,far);
 90     edge[a.center].push_back(b.center);
 91     edge[b.center].push_back(a.center);
 92     flag =  0;
 93     dfs2(0,a.site,b.site);
 94     ans.push_back(make_pair(a.center,b.center));
 95     }
 96     tree = pq.top();
 97     cout<<tree.dig<<endl;
 98     
 99     for(auto it = ans.begin();it!=ans.end();it++) {
100     cout<<it->first<<" "<<it->second<<endl;
101     }
102     
103 
104 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM