题目描述
跳蚤王国爆发了一场动乱,国王在镇压动乱的同时,需要在跳蚤国地方钦定一个人来做宰相。
由于当时形势的复杂性,很多跳蚤都并不想去做一个傀儡宰相,带着宰相的帽子,最后还冒着被打倒并杀头的危险,然而有一只跳蚤却想得与众不同最时尚。
本来他打算去教书,他已经发表了自己在学术方面的见解,获得了很多跳蚤们的赞同,但是这时听说跳蚤国要钦定宰相,他毅然打断了想去教书的想法,他觉得只要为了国家利益,自己的生死都可以不管,哪里能因为工作能给自己带来灾祸或者福分就去避开或者接近这份工作呢?所以他决定站出来接了这份工作。
然而当时国王的钦定方式很奇怪,跳蚤王国可以看作一棵树,国王认为宰相必须更好的为跳蚤服务,所以他会选择一个到所有节点距离和最小的节点,并在这个节点中钦定,如果有多个节点满足距离和最小则任选一个。
然而跳蚤国的动乱实在是太厉害了,以至于树的形态可能也会发生改变,也就是说,树上可能会有若干条边消失,如果这个情况出现的话一定会有同样数目的边出现,以保证整个结构仍然是一棵树。
现在这个跳蚤想知道每个节点中的跳蚤如果要被钦定,至少需要多少条边消失(当然也会有同样数目的边出现)。作为这只跳蚤的一名真正的粉丝,你能帮他解决这个问题吗?
数据范围:n≤10^6
题目分析
首先分析这里被钦定节点的性质。对于边$(u,v)$,如果$u$比$v$更优,即$u$一侧的节点数多于$v$一侧的节点数。容易发现任选一个点开始这个过程,其转移都是向最优解单调增的。那么最后停止的充要条件就是其任一相连子树的大小$size$都满足$size\le n-size$,也就是说“被钦定的节点”就是这棵树的重心。
于是问题变为了修改最少的边使指定节点成为重心。那么有一个很直观的想法就是,把原重心的子树“割给”指定节点。在具体的实现中,就是对于重心向相邻子树遍历过去,若遍历的子树未被选取,就是把“能起到压制作用”的子树给它;若遍历的子树被选取,就要在重心到遍历节点的路径上割一刀以实现重心。
写这类题的时候±1之类的细节在码题之前就需要注意,不然在写的时候调会越调越乱……
#include<bits/stdc++.h>
const int maxn = ;
const int maxm = ; int n,root,cnt,leg,sum;
int size[maxn],son[maxn],anc[maxn],ans[maxn];
int edgeTot,head[maxn],nxt[maxm],edges[maxm]; int read()
{
char ch = getchar();
int num = , fl = ;
for (; !isdigit(ch); ch=getchar())
if (ch=='-') fl = -;
for (; isdigit(ch); ch=getchar())
num = (num<<)+(num<<)+ch-;
return num*fl;
}
void addedge(int u, int v)
{
edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot;
}
void getRoot(int x, int fa, bool tag)
{
son[x] = , size[x] = ;
for (int i=head[x]; i!=-; i=nxt[i])
{
int v = edges[i];
if (v==fa) continue;
getRoot(v, x, tag), size[x] += size[v];
son[x] = std::max(son[x], size[v]);
}
son[x] = std::max(son[x], n-size[x]);
if (tag&&son[x] < son[root]) root = x;
}
bool cmp(int x, int y)
{
return size[x] > size[y];
}
void dfs(int x, int fa, int c)
{
ans[x] = (leg-)+((n-c-size[x])* > n);
for (int i=head[x]; i!=-; i=nxt[i])
if (edges[i]!=fa) dfs(edges[i], x, c);
}
int main()
{
memset(head, -, sizeof head);
n = read();
for (int i=; i<n; i++) addedge(read(), read());
root = , son[root] = n;
getRoot(, , true);
getRoot(root, , false);
for (int i=head[root]; i!=-; i=nxt[i])
anc[++cnt] = edges[i];
std::sort(anc+, anc+cnt+, cmp);
for (int i=; i<=cnt; i++)
{
sum += size[anc[i]], ++leg;
if (sum* >= (n)) break;
}
for (int i=; i<=cnt; i++)
dfs(anc[i], root, sum-std::max(size[anc[i]], size[anc[leg]]));
for (int i=; i<=n; i++) printf("%d\n",ans[i]);
return ;
}
END