UOJ387 【UNR #3】To Do Tree 题解
题意:
有一棵 \(n\) 个节点的有根树,\(1\) 号点为根节点。第 \(i\) 的父节点为 \(f_i\) (\(1\leq f_i < i \leq n\))。你每星期可以选择不超过 \(m\) 个节点删掉,需要保证被删掉的节点是没有父节点的。删完后,会剩下一个森林 (若干棵树)。求最少多少星期才能删完所有的节点。
输入格式:
第一行包含两个正整数 \(n,m\) (\(1\leq m\leq n\le 10^5\))。
第二行包含 \(n-1\) 个正整数 \(f_2,f_3,\cdots,f_n\) (\(1\leq f_i<i\leq n\))。
输出格式:
第一行输出一个整数 \(t\),表示最少需要的时间。
接下来 \(t\) 行,第 \(i\) 行表示第 \(i\) 星期删的点,其中第一个整数 \(s_i\) 表示这周删的点的个数,接下来 \(s_i\) 个整数分别是这周肝的点的标号。
考虑贪心,每次选出子树大小最大的 \(m\) 个删掉。
正确性严谨证明参考OI中转站 ,反正我是凭感觉的,因为子树大小越大,删掉根后树的数量的期望更高。
时间复杂度 \(\mathcal{O}(n\log n)\)
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
typedef pair<int,int> pii;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5 + 15))
int n,k,fa[N],sz[N],buf[N],len[N];
vector<int> vec[N]; priority_queue<pii> q;
void addEdge(int u,int v) { vec[u].push_back(v); }
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> k; int *f = buf, tot = 0;
for(int i = 2; i <= n; i++) { cin >> fa[i], addEdge(fa[i], i); }
for(int i = n; i; i--) sz[fa[i]] += (++sz[i]);
for(q.emplace(sz[1], 1); !q.empty(); f += len[tot++])
{
int &cnt = len[tot];
for(int i = k; !q.empty() && i; i--) {
f[cnt++] = q.top().second, q.pop();
}
for(int i = 0; i < cnt; i++)
for(int v : vec[f[i]]) q.emplace(sz[v], v);
}
cout << tot << '\n'; f = buf;
for(int i = 0; i < tot; f += len[i++])
{
cout << len[i] << ' ';
for(int v = 0; v < len[i]; v++)
cout << f[v] << " \n"[v + 1 == len[i]];
}
return 0;
}