CF1039D You Are Given a Tree 根号分治+二分

如果 $k$ 值确定的话,我们直接来一个 $O(n)$ 的贪心就行.  

那么我们就将 $k$ 分为大于 $B$ 和小于 $B$ 两部分处理.  

对于小于 $B$ 的部分,暴力处理,复杂度为 $O(nB)$.  

对于大于 $B$ 的部分,取值分别为 $[0,\frac{n}{B}]$ 且依次递减.  

那么我们就可以二分每种取值的区间.   

由于有 $\frac{n}{B}$ 种取值,计算每个取值区间为 $O(n \log n)$,故这部分复杂度为 $\frac{n}{B} n \log n$    

一般来说,这个 $B$ 取 200 左右是比较优的. 

code: 

#include <bits/stdc++.h>    
#define B 203   
#define N 100006 
#define ll long long   
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;  
int n,edges,tim;  
int hd[N],to[N<<1],nex[N<<1],f[N],vis[N],maxx[N],po[N],fa[N];    
void add(int u,int v) 
{
	nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;  
}   
void dfs(int x,int ff) 
{ 
	po[++tim]=x,fa[x]=ff;   
	for(int i=hd[x];i;i=nex[i])   
		if(to[i]!=ff)  
			dfs(to[i],x);   
}       
int solve(int x) 
{      
	f[x]=0;      
	int i,j;  
	memset(vis,0,sizeof(vis));   
	for(i=1;i<=n;++i) maxx[i]=1;  
	for(i=tim;i>=1;--i) 
	{     
		int u=po[i];                  
		if(fa[u]&&!vis[u])      
		{
			if(vis[fa[u]]==0) 
			{
				if(maxx[fa[u]]+maxx[u]>=x)    
					++f[x],maxx[fa[u]]=0,vis[fa[u]]=1;       
				else          
					maxx[fa[u]]=max(maxx[fa[u]],maxx[u]+1);    
			}                    
		}
	}    
	return f[x];             
}
void init(int M) 
{   
	int i,j;     
	f[1]=n;     
	for(i=2;i<=M;++i)    
	   	solve(i);     
}
int main() 
{ 
	// setIO("input");  
	int i,j;       
	scanf("%d",&n);   
	for(i=1;i<n;++i) 
	{
		int x,y;  
		scanf("%d%d",&x,&y);  
		add(x,y),add(y,x);  
	}             
	dfs(1,0);   
	init(min(n,B));    
	if(n<=B) 
	{
		for(i=1;i<=n;++i)  
			printf("%d\n",f[i]);  
	}   
	else 
	{
	    int An;  
		for(i=B+1;i<=n;i=An+1) 
		{ 
			An=i;  
			int l=i,r=n;   
			int tmp=solve(l);  
			while(l<=r) 
			{  
				int mid=(l+r)>>1;   
				if(solve(mid)==tmp)    
					An=mid,l=mid+1;   
				else  
					r=mid-1;   
			}    
			for(j=i;j<=An;++j) 
				f[j]=tmp;  
		}
		for(i=1;i<=n;++i)  
			printf("%d\n",f[i]);  
	}
	return 0;
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值