[CF1039D]You Are Given a Tree
Description:
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;
}