核心思想 : topo
看了下好像题解都是写以直径为基础的算法,我来讲一下鄙人自己对于这道题的认识。
如图,这是一颗满足题目要求的无根树
这时,如果 \(k=2\) 那么显然 2 号城市和 4 号城市会成为核心城市,原因是此时所有非叶子节点都是非核心城市,答案为 1。
那么来看看下面这个图:
先设它的根为 1 号节点。
让我们看看 \(k=13\) 时是怎样的,很明显,所有的城市都可以成为核心城市,所以答案为0。
那当 \(k=7\) 时呢(有 6 个城市被排出在外时)?
你可以这样,此时答案是1且最优。
你也可以这样,此时答案是1且最优。
多画几个图,不难发现,当 \(5 \leq k \leq 12\) 时,答案都是1,此时所有非核心城市都有一个共同点:都是叶子节点(如下图)。
于是我们就可以想到由叶子节点一层层往内推统计城市个数,最后答案就是所有非核心城市的深度的最大值,这里的深度是由叶子节点为为第一层往根上推的,而根是什么根本无所谓。
是不是感觉和 topo 想法很像。
上代码!!!
# include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
const int maxe = 200005;
struct reader {
template <typename Type>
reader&operator>> (Type&ret) {
register int f = 1; ret = 0; register char ch = getchar ();
for (;!isdigit (ch); ch = getchar ()) if (ch=='-') f=-f;
for (; isdigit (ch); ch = getchar ()) ret = (ret << 1) + (ret << 3) + ch - '0';
ret *= f; return *this;
}
}fin;// 快读
int N, K, x, y, du[maxn], ans;
int lnk[maxn], nxt[maxe], son[maxe], tot;
void add_e (register int x, register int y) {
son[++tot] = y; nxt[tot] = lnk[x];
lnk[x] = tot; du[y]++; return;
} // 邻接表存图
int que[maxn], dep[maxn], L, R; bool vis[maxn];
//这里的变量和数组都是字面意思,初中的教练一直要求代码要可以断章取义
void topo () {
while (L ^ R) {
L++;
for (register int j = lnk[que[L]]; j; j = nxt[j]) {
if (--du[son[j]] != 1) continue;
//这一句不能落掉,是 topo 函数的关键
if (vis[son[j]]) continue; vis[son[j]] = true;
dep[son[j]] = dep[que[L]] + 1; ans = max (ans, dep[son[j]]);
//这里随时刷新ans的最大值,可以防止后面再刷一遍
que[++R] = son[j]; K--; if (K < 1) return;
}
}
return;
} //其他的就是普通的 topo 写法了
int main () {
freopen("P5536.in","r",stdin);
freopen("P5536.out","w",stdout);
fin >> N >> K; K = N - K; // 将 K 的含义转为非核心城市的个数
for (register int i = 2; i <= N; i++) {
fin >> x >> y;
add_e (x, y); add_e (y, x);
} L = R = 0; ans = 0; // 建图
for (register int i = 1; i <= N; i++) if (du[i] == 1 && K >= 1)
que[++R] = i, vis[i] = true, K--, ans = 1, dep[i] = 1;
//这一块原本是应该放在topo函数中的,但是因为加了个特判,所有写在主函数里
if (K) topo (); cout << ans << endl;
return 0;
}
最后,安利一波这个画图网站,真的超级好!
Made By \(\operatorname{wheneveright}\)