1. 程式人生 > >[CF1042F]Leaf Sets

[CF1042F]Leaf Sets

next 只有一個 代碼 一個 col 距離 tchar 理解 clu

題意:給定一棵$n$個點的樹,將葉子節點分為數個集合使集合裏點對最長距離不超過$k$,求最少集合數。($n\le1000000$)

首先我們可以想到,這道題並不是讓你構造最優方案,因為只要把所有葉子節點的集合任意合並至無法操作,就一定是最優答案了

這個感性理解一下就是那麽回事,我一開始做的時候就想到的

然後其實我們把葉子結點向上合並即可,只要子樹內兩個集合的距離不超過$k$,就把他們合起來,記成到當前點距離較大的那個集合

這樣對於子樹兩兩合並,我們可以用啟發式合並的堆來實現,復雜度是$nlog^2n$,並不足以通過這道題

再想想看,其實如果一個集合和當前最小的集合已經無法合並了,就不用再往上推了,因為上邊的集合的最遠點只會更遠,所以把這個集合留住在這,答案加一

然後我們可以把子樹內兩個集合的距離不超過$k$的集合合並起來,這樣我們每次向上傳遞的只有一個集合了,復雜度$nlogn$

就從一道大數據結構題變成了一道思維難度更大的題了

代碼:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<queue>
 4 #define M 1000010
 5 using namespace std;
 6 int read()
 7 {
 8     char ch=getchar();int x=0;
 9     while(ch>9
||ch<0) ch=getchar(); 10 while(ch>=0&&ch<=9) x=x*10+ch-0,ch=getchar(); 11 return x; 12 } 13 int n,num,ans,k; 14 int head[M],in[M]; 15 struct point{int to,next;}e[M<<1]; 16 void add(int from,int to) 17 { 18 e[++num].next=head[from]; 19 e[num].to=to;
20 head[from]=num; 21 } 22 int dfs(int x,int fa) 23 { 24 if(in[x]==1) return 0; 25 priority_queue<int>q; 26 for(int i=head[x];i;i=e[i].next) 27 { 28 int to=e[i].to; 29 if(to==fa) continue; 30 q.push(dfs(to,x)+1); 31 } 32 int maxn=q.top();q.pop(); 33 while(!q.empty()) 34 { 35 if(q.top()+maxn<=k) return maxn; 36 ans++; 37 maxn=q.top(); 38 q.pop(); 39 } 40 return maxn; 41 } 42 int main() 43 { 44 n=read(); k=read(); 45 for(int i=1;i<n;i++) 46 { 47 int a=read(),b=read(); 48 add(a,b); add(b,a); 49 in[a]++; in[b]++; 50 } 51 for(int i=1;i<=n;i++) if(in[i]!=1){dfs(i,0);break;} 52 printf("%d",ans+1); 53 return 0; 54 }

[CF1042F]Leaf Sets