嘘~ 正在从服务器偷取页面 . . .

UOJ387 【UNR #3】To Do Tree 题解


UOJ387 【UNR #3】To Do Tree 题解

题目链接:#387. 【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;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录