zoukankan      html  css  js  c++  java
  • 【CF1039D】You Are Given a Tree

    题目

    题目链接:https://codeforces.com/contest/1039/problem/D
    有一棵 (n) 个节点的树。其中一个简单路径的集合被称为 (k) 合法当且仅当树的每个节点至多属于其中一条路径,且每条路径恰好包含 (k) 个点。
    对于 (kin [1,n]),求出 (k) 合法路径集合的最多路径数。
    即:设 (k) 合法路径集合为 (S),求最大的 (|S|)
    (n leq 10^5)

    思路

    考虑当 (k) 固定的时候怎么搞。设 (f[x]) 表示 (x) 子树内到 (x) 没有点用过的一条路径最长的长度。合并两棵子树时,如果 (f[x]+f[y]+1geq k),那么就贪心合并这两条路径并让答案加一,(f[x]) 赋值为负无穷。
    这样的贪心策略正确性显然,因为如果可以合并但是不合并而是把这一段长度给到父节点肯定不会更优。
    暴力做是 (O(n^2)) 的。考虑根号分治。
    设一个阈值 (M)。当 (kleq M) 的时候暴力求。当 (k>M) 的时候,答案肯定在 ([0,lfloorfrac{n}{M} floor]) 内,而随着 (k) 的增大,答案是不增的。
    那么假设当前求 (i) 的答案 (res),我们可以二分一个右端点 (j) 满足它是最大的答案等于 (res) 的点。那么 ([i,j]) 的答案一定都等于 (res)
    二分次数是 (O(frac{n}{M})) 的,每一次二分的复杂度都是 (O(nlog n))。再加上前面暴力的复杂度,总时间复杂度为 (O(nM+frac{n^2log n}{M}))。取 (M=sqrt{nlog n}) 时有最优复杂度 (O(nsqrt{nlog n}))
    这道题有点卡常,可以按照 dfs 序来循环 dp。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=100010,M=1300,Inf=1e9;
    int n,tot,head[N],dfn[N],fa[N],f[N];
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    void dfs(int x,int fat)
    {
    	dfn[++tot]=x; fa[x]=fat;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=fat) dfs(v,x);
    	}
    }
    
    int solve(int k)
    {
    	int ans=0;
    	for (int j=n;j>=1;j--)
    	{
    		int x=dfn[j]; f[x]=0;
    		for (int i=head[x];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (v!=fa[x])
    			{
    				if (f[v]+f[x]+1>=k) ans++,f[x]=-Inf;
    				if (f[x]!=-Inf) f[x]=max(f[x],f[v]);
    			}
    		}
    		f[x]++;
    	}
    	return ans;
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d",&n);
    	for (int i=1,x,y;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y); add(y,x);
    	}
    	tot=0; dfs(1,0);
    	cout<<n<<"
    ";
    	for (int i=2;i<=min(n,M);i++)
    		cout<<solve(i)<<"
    ";
    	for (int i=M+1;i<=n;)
    	{
    		int l=i,r=n,mid,res=solve(i);		
    		while (l<=r)
    		{
    			mid=(l+r)>>1;
    			if (solve(mid)==res) l=mid+1;
    				else r=mid-1;
    		}
    		for (;i<l;i++) cout<<res<<"
    ";
    	}
    	return 0;
    }
    
  • 相关阅读:
    Web前端开发中的各种CSS规范
    SVN简明课程
    使用django-compressor压缩静态文件
    今日头条视频Url嗅探
    python 异常类型
    抓包分析工具备注
    电子签章盖章之jQuery插件jquery.zsign
    程序员读书雷达
    在csdn里markdown感受
    如何在无趣的世界里,做一个有趣的人?
  • 原文地址:https://www.cnblogs.com/stoorz/p/15060017.html
Copyright © 2011-2022 走看看