• CF1039D You Are Given a Tree


    这个题好像贪心做法就不太会。/wq
    果然是因为自己太菜了吗。
    因为这是一个根号分治的题目,不过好像也可以整体二分,整体二分还在学,如果学了会回来再做一遍这个题。
    先考虑写出一个贪心,考虑如果有能拼起来大于 (k) 的链,我们就拼起来,那么这样一次操作是 (O(n))
    如果我们对所有答案都进行操作的话,是一个 (O(n ^ 2)) 的复杂度。
    我们考虑如果我们把 (k <= T) 的操作直接暴力做,那这是一个 (O(nT)) 的东西。
    我们考虑对于 (k) 来说,他的答案是有单调性的,我们对 (k >= T) 考虑二分,二分到最右边那个答案一样的点,那这是一个(O(frac{n^2logn}{T}))的东西
    (T = sqrt(nlogn)) 较优
    二分之后往需要的那一边扩展是我的一个习惯,害怕有些地方二分会挂掉,毕竟不同的二分的话,和正确答案的差别不超过(2)左右,所以其实没有慢多少。

    CF1039D You Are Given a Tree
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #define ll long long
    #define N 100005
    
    ll n;
    
    ll head[N],cnt;
    
    struct P{
    	int to,next;
    }e[N * 2];
    
    inline void add(int x,int y){
    	e[++cnt].to = y;
    	e[cnt].next = head[x];
    	head[x] = cnt;
    }
    
    ll fa[N],dfn[N],dfncnt;
    
    inline void dfs(int u,int f){
    	fa[u] = f;
    	for(int i = head[u];i;i = e[i].next){
    		int v = e[i].to;
    		if(v == f)
    		continue;
    		dfs(v,u);
    	}
    	dfn[++dfncnt] = u;
    }
    
    ll f[N];//该点下最长有多少链 
    
    inline ll solve(int k){
    	ll ans = 0;
    	for(int i = 1;i <= n;++i)
    	f[i] = 1;
    	for(int i = 1;i <= n;++i){
    		int u = dfn[i];
    		int fi = fa[u];
    		if(fi && f[u] != -1 && f[fi] != -1)
    		if(f[u] + f[fi] >= k)
    		ans ++ ,f[fi] = -1;
    		else
    		f[fi] = std::max(f[fi],f[u] + 1);
    	}
    	return ans;
    }
    
    ll ans[N];
    
    int main(){
    	scanf("%lld",&n);
    	for(int i = 1;i < n;++i){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);add(y,x);
    	}
    	dfs(1,0);
    	ll s = std::sqrt(n * log2(n));
    	ans[1] = n;//这里因为不用和父亲链,所以直接solve可能会出事。 
    	for(int i = 2;i <= s;++i)
    	ans[i] = solve(i);
    	for(int i = s + 1;i <= n;++i){
    		ans[i] = solve(i);
    		int l = i,r = n,to = i;
    		while(l <= r){
    			ll mid = (l + r) >> 1;
    			if(solve(mid) == ans[i])
    			l = mid + 1,to = mid;
    			else
    			r = mid - 1;
    //			std::cout<<l<<" "<<r<<std::endl;
    		}
    		to -= 1;//用一点时间换答案正确。 
    		while(solve(to + 1) == ans[i] && to <= n)
    		to ++ ;
    		for(int j = i + 1;j <= to;++j)
    		ans[j] = ans[i];
    		i = to; 
    	}
    	for(int i = 1;i <= n;++i)
    	std::cout<<ans[i]<<std::endl;
    }
    
  • 相关阅读:
    C#的一些基本问题
    Mac ssh连接远程服务器,并实现文件的上传和下载
    Redis 持久化
    Redis 数据类型
    @dynamic 与 @synthesize 关键词详解
    Redis介绍及安装
    crontab的用法
    修改文件权限
    Linux目录结构
    一些命令
  • 原文地址:https://www.cnblogs.com/dixiao/p/14761858.html
Copyright © 2020-2023  润新知