把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷3441】[POI2006] MET-Subway(树的拓扑)

点此看题面

  • 给定一棵\(n\)个点的树,求用\(l\)条树上路经最多能覆盖多少个点。
  • \(n\le10^6\)

拓扑分层

显然路径的端点一定是叶节点(准确的说,由于这是无根树,应该是度数为\(1\)的节点),因为如果端点不是叶节点则路径必然可以继续拓展,答案不会变劣。

所以有了一个从叶节点向上逐层考虑的想法,但一个致命问题就在于叶节点的深度不一定相同,如果叶节点分散在很多层的话没法进行一个统一的处理。

这里就有一个非常奇妙的应对策略:我们不一定要让每个点的父节点与它在相邻层。

可以假定所有叶节点在同一层,然后跑一遍拓扑排序,定义一个点所在层数是它的所有子节点中最大的层数加\(1\)

接着我们再回到最开始的想法,假设第\(i\)层点数为\(cnt_i\),显然第\(i\)层最多有\(\min\{cnt_i,2l\}\)个点被覆盖,而且每一层都能达到这个上界,这应该是很容易就能构造出来的。

代码:\(O(n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,l,ee,lnk[N+5],deg[N+5];struct edge{int to,nxt;}e[2*N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
}using namespace FastIO;
int q[N+5],rk[N+5],cnt[N+5];I void Topo()//拓扑排序
{
	RI i,k,H=1,T=0;for(i=1;i<=n;++i) deg[i]==1&&(q[++T]=i);//从叶节点开始拓扑
	W(H<=T) for(++cnt[rk[k=q[H++]]],i=lnk[k];i;i=e[i].nxt) --deg[e[i].to]==1&&(rk[q[++T]=e[i].to]=rk[k]+1);//子节点中最大层数+1
}
int main()
{
	RI i,x,y;for(read(n),read(l),i=1;i^n;++i) read(x),read(y),add(x,y),add(y,x),++deg[x],++deg[y];
	RI t=0;for(Topo(),i=0;i<=n;++i) t+=min(cnt[i],l<<1);return printf("%d\n",t),0;//第i层选择min{cnt[i],2l}个点
}
posted @ 2021-06-03 15:06  TheLostWeak  阅读(62)  评论(0编辑  收藏  举报