题意
ZBQC 要建一个新的厕所了!
解决资金问题后,现在摆在面前的是又一个大难题:厕所建在哪儿?
ZBQC 是个大中学,有 N N N ( N N N <= 2e5)间教室,由于学校植被较多,空间比较紧凑,因此,这 N N N 间教室由 N N N-1 条等长的双向道路连通。现在一些教室有班级(一间教室只能有一个班级),一些是空教室,每个班级到这个厕所有一定路程(即通过的道路条数)。如果某间教室满足所有班级到这里的路程和最小,那么这间教室就是最佳施工地点,厕所可以紧挨着它建。当然,最佳施工地点可能有很多个。
但是,考虑到新学年快要来了,新的学年那些教室有班级,一共多少个班级都不清楚。所以,工程师想知道:对于每个 i ∈ [ 1 , N ] i∈[1,N] i∈[1,N] ,当班级总数为 i i i 时,最佳施工地点最多能有多少个?工程师求助了 OI 最强 JZM。
但是 JZM 正在去国赛的路上堵车,心情不好想睡觉,于是把这个问题丢给了 PPL 。
PPL 打了个暴力,但是由于跑太快了,还没看到结果,程序就结束关闭了,他只好又把问题抛给 ZXY。
ZXY 瞬间就码出 O ( 1 ) O(1) O(1) 代码来了,正在跑……现在还在跑,于是 PPL 见 ZXY 靠不住,又求助 SY。
SY 想出了个新做法,配合他的新数据结构,用他的新码风码了起来……现在还在码。
于是 PPL 只好求助你。
题解
首先有一个结论, i i i 为奇数时答案一定为 1 。这个通过调整法可以很容易地证明出来,不展开。
然后是 i i i 为偶数时,如果答案大于 1 的话,一定是在这样一个子图中:一条长度为答案-1的链,链两端吊着两棵树,每棵树里各有 i / 2 i/2 i/2 个关键点。
这样一来,我们可以证明答案的单调性: a n s [ i ∗ 2 ] > = a n s [ ( i + 1 ) ∗ 2 ] ans[i*2]>=ans[(i+1)*2] ans[i∗2]>=ans[(i+1)∗2]。接下来怎么求呢,我们不妨分类讨论贡献:
- 链两端有祖先—后代关系:我们可以把每个点作为下端时按照子树从大到小排序,每条边作为上端时按照 N − S i z e s o n ( 总 点 数 − 下 端 子 树 大 小 ) N-Size_{son}(总点数-下端子树大小) N−Sizeson(总点数−下端子树大小) 排序,然后从大到小枚举 SIZE 同时加合法点和加合法边,加点时产生的贡献可以通过倍增找最远的合法的祖先计算,加边时可以找边下端子树内深度最大的合法点计算(按照 dfs 序拍到线段树上)。这里某个点的合法祖先,是指以此点为根时该祖先的子树大小不小于我们现在枚举的 SIZE ,合法点是指子树大小不超过 SIZE 的点,因此我们枚举到新的 SIZE 时应该先加点再加边。时间复杂度 O ( n log n ) O(n\log n) O(nlogn) 。
- 链两端分别在不同的子树内,没有祖先—后代关系:这种是最麻烦的,但是我们可以树 DP:令 d p [ i ] [ j ] dp[i][j] dp[i][j] 为 i 子树内 Size 大于等于 j 的点的最大深度, f [ i ] [ j ] f[i][j] f[i][j] 表示 i 子树内限制 SIZE 为 j 时的最大答案。 d p [ i ] [ j ] dp[i][j] dp[i][j] 可以直接从重儿子继承过来,然后 DSU on tree 暴力更新,同时就可以更新 f [ i ] [ j ] f[i][j] f[i][j] 了。时间复杂度 O ( n log n ) O(n\log n) O(nlogn) 。
CODE
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define SI set<cp>::iterator
#define lowbit(x) (-(x) & (x))
#pragma GCC optimize(2)
#define int LL
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {
if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {
x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 1000000007;
int n,m,i,j,s,o,k;
int ANS[MAXN];
int tre[MAXN<<2],M;
void maketree(int n) {
M=1;while(M<n+2)M<<=1;}
void addtree(int x,int y) {
int s = M+x;tre[s] = y;s >>= 1;
while(s) tre[s] = max(tre[s<<1],tre[s<<1|1]),s >>= 1;
}
int findtree(int l,int r) {
if(l > r) return 0;
int s = M+l-1,t = M+r+1,as = 0;
while(s || t) {
if((s>>1) ^ (t>>1)) {
if(!(s & 1)) as = max(as,tre[s^1]);
if(t & 1) as = max(as,tre[t^1]);
}else break;
s >>= 1;t >>= 1;
}
return as;
}
vector<int> qp[MAXN],qe[MAXN];
vector<int> g[MAXN];
int d[MAXN],siz[MAXN],f[MAXN][20],son[MAXN],tp[MAXN],dfn[MAXN],rr[MAXN],tim;//18
int fp[MAXN];
void dfs0(int x,int ff) {
// d,siz,f,son,
d[x] = d[f[x][0] = ff] + 1;
for(int i = 1;i <= 18;i ++) f[x][i] = f[f[x][i-1]][i-1];
siz[x] = 1; son[x] = 0; tim = 0;
for(int i = 0;i < (int)g[x].size();i ++) {
int y = g[x][i];
if(y != ff) {
dfs0(y,x);
siz[x] += siz[y];
if(siz[y] > siz[son[x]]) son[x] = y;
}
}
qp[siz[x]].push_back(x);
return ;
}
void dfs1(int x,int ff) {
// tp,dfn,rr,fp
if(son[ff] == x) tp[x] = tp[ff];
else tp[x] = x;
if(ff) {
fp[x] = n - siz[x];
qe[fp[x]].push_back(x);
}
dfn[x] = ++ tim;
if(son[x]) dfs1(son[x],x);
for(int i = 0;i < (int)g[x].size();i ++) {
int y = g[x][i];
if(y != ff && y != son[x]) {
dfs1(y,x);
}
}
rr[x] = tim;
return ;
}
int dpp[MAXN],maxd;
int dp1[MAXN],dp2[MAXN],le;
void cont(int x,int ff) {
dpp[siz[x]] = max(dpp[siz[x]],d[x]);
maxd = max(maxd,siz[x]);
for(int i = 0;i < (int)g[x].size();i ++) {
int y = g[x][i];
if(y != ff) {
cont(y,x);
}
}return ;
}
void dfs2(int x,int ff) {
if(siz[x] == 1) {
dp1[1] = d[x];
dp2[1] = -0x7f7f7f7f; le = 1;
return ;
}
for(int i = 0;i < (int)g[x].size();i ++) {
int y = g[x][i];
if(y != ff && y != son[x]) {
dfs2(y,x);
}
}
le = 1;
dfs2(son[x],x);
while(le < siz[x]) le ++, dp1[le] = d[x],dp2[le] = -0x7f7f7f7f;
while(maxd > 0) dpp[maxd --] = -0x7f7f7f7f;
for(int i = 0;i < (int)g[x].size();i ++) {
int y = g[x][i];
if(y != ff && y != son[x]) {
while(maxd > 0) dpp[maxd --] = -0x7f7f7f7f;
cont(y,x);
int mx = -0x7f7f7f7f;
for(int i = maxd;i > 0;i --) {
mx = max(mx,dpp[i]);
dp2[i] = max(dp2[i],dp1[i] + mx - d[x] - d[ff]);
dp1[i] = max(dp1[i],mx);
}
}
}
dp1[siz[x]] = d[x];
if(tp[x] == x) {
for(int i = 1;i <= le;i ++) {
ANS[i] = max(ANS[i],dp2[i]);
}
}
return ;
}
//-------------------
int maxtmp = 0;
void addp(int x,int pnm) {
addtree(dfn[x],d[x]);
int ff = x;
for(int i = 18;i >= 0;i --) {
if(fp[f[ff][i]] >= pnm) {
ff = f[ff][i];
}
}
if(fp[x] >= pnm) maxtmp = max(maxtmp,d[x] - d[ff] + 2);
return ;
}
void adde(int x) {
int mxd = findtree(dfn[x],rr[x]);
if(mxd > 0) maxtmp = max(maxtmp,mxd - d[x] + 2);
return ;
}
signed main() {
n = read();
for(int i = 1;i < n;i ++) {
s = read();o = read();
g[s].push_back(o);
g[o].push_back(s);
}
dfs0(1,0);
dfs1(1,0);
dfs2(1,0);
maketree(n);
int tg = n+1; maxtmp = 1;
for(int i = n/2;i > 0;i --) {
while(tg > i) {
tg --;
for(int j = 0;j < (int)qp[tg].size();j ++) {
int y = qp[tg][j];
addp(y,i);
}
for(int j = 0;j < (int)qe[tg].size();j ++) {
int y = qe[tg][j];
adde(y);
}
}
ANS[i] = max(ANS[i],maxtmp);
maxtmp = max(ANS[i],maxtmp);
}
for(int i = 1;i <= n;i ++) {
if(i & 1) {
printf("1\n");
}
else {
printf("%lld\n",ANS[i>>1]);
}
}
return 0;
}