[CF1042F]Leaf Sets

题意:给定一棵$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 }

猜你喜欢

转载自www.cnblogs.com/Slrslr/p/9689085.html
今日推荐