[CF1039D]You Are Given a Tree

Description:

a.png

Solution:

\(O(n^2)\) 的做法就是每次 dfs 整棵树 贪心从下往上选,具体而言就是维护以 u 儿子传上来的最长链和次长链,如果最长链 + 次长链 + 1 \(\geq\) \(k\) 则拼成一条长度为 \(k\) 的链,然后穿一条长度为 0 的上去,否则把最长链传上去。

正确性:由于当前存在最长链 + 次长链 +1\(\geq\) \(k\),就算传一条最长链上去也只可能使答案增加1,而不如现在就让最长链匹配了次长链产生1的贡献,可能会更优。

发现当\(k\geq \sqrt n\)时,答案 \(\le \sqrt n\),而且不难发现答案随着k变大变小,并且由一些连续段拼接而成,我们对于每个答案二分其最右边的端点使得这一块的答案一样,所以总时间复杂度为 \(O(n\sqrt n\log n)\)

这题比较卡常,不能写递归的 dfs,把每个点的 dfn 序搞出来就可以用 for 循环实现了。

Code:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>

using namespace std;

#define LL long long
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define REP(i, a, b) for (register int (i) = (a); (i) <= (b); ++(i))
#define GO cerr << "GO" << endl;

inline void proc_status()
{
	ifstream t("/proc/self/status");
	cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
template<class T> inline T read() 
{
	register char c;
	register T x(0), f(1);
	while (!isdigit(c = getchar())) if (c == '-') f = -1;
	while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
	return x * f;
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

const int maxN = (int) 1e5;

int n;
int ans[maxN + 2],ver[maxN<<1],nxt[maxN<<1],head[maxN+2],tot,dfst,rev[maxN+2];
int first[maxN+2],second[maxN+2],fa[maxN+2];

void link(int u, int v)
{ ver[++tot]=v,nxt[tot]=head[u],head[u]=tot; }

void Input()
{
	n = read<int>();
	for (int i = 1; i < n; ++i)
	{
		int u = read<int>(), v = read<int>();
		link(u, v), link(v, u);
	}
}

void dfs(int u, int f)
{
	rev[++dfst]=u;
	fa[u]=f;
	for (int i = head[u];i;i=nxt[i])
	{
		int v=ver[i];
		if(v==f)continue;
		dfs(v,u);
	}
}

inline int Calc(int l)
{
	memset(first,0,4*(n+1));
	memset(second,0,4*(n+1));
	int res(0);
	for (register int i = n; i >= 1; --i)
	{
		int u=rev[i],f=fa[u],len;
		if(first[u]+second[u]+1>=l) ++res,len=0;
		else len=first[u]+1;
		if (!f) continue;
		if(first[f] < len)second[f]=first[f],first[f]=len;
		else if(second[f]<len)second[f]=len;
	}
	return res;
}

void Solve()
{
	int i;
	int sqr = int(sqrt(n));

	dfs(1,0);
	for (i = 1; i <= sqr; ++i) ans[i] = Calc(i);
	for (; i <= n; ++i)
	{
		ans[i]=Calc(i);
		int l=i,r=n,pos=i,mid;
		while (l<=r)
		{
			mid=(l+r)>>1;
			if(Calc(mid)==ans[i])pos=mid,l=mid+1;
			else r=mid-1;
		}
		for(int j=i+1;j<=pos;++j)ans[j]=ans[i];
		i=pos;
	}
	for(int i=1;i<=n;++i)
		printf("%d\n",ans[i]);
}

int main() 
{ 
	Input();
	Solve();
	return 0;
}
posted @ 2019-09-20 08:38  茶Tea  阅读(160)  评论(0编辑  收藏  举报