Educational Codeforces Round 136 (Rated for Div. 2)

230 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
本文已参与「新人创作礼」活动,一 起开启掘金创作之路。
## D. Reset K Edges

给你一棵有根的树,由n个顶点组成。这些顶点的编号从1到n,根是顶点1。

你最多可以执行以下操作k次。

选择树的一条边(v,u),使v是u的父级。 删除边(v,u)。 增加一条边(1,u)(即让u和它的子树成为根的孩子)。 树的高度是其顶点的最大深度,而顶点的深度是根到它的路径上的边的数量。例如,顶点1的深度是0,因为它是根,而它所有的子节点的深度是1。

可以达到的最小的树的高度是多少?

对于这种类型的题目我们一般采用二分的写法

所以先把二分框架搭好

   int l = 1,r = 4e5;
   while( l <= r)
   {
     int mid = l + r >> 1;
     if(check(mid)) r = mid - 1;
     else l = mid + 1;
   }

所以题目的难点来到了check函数的书写

我们运用贪心的思想从下往上遍历结点发现一个节点的最大深度等于k那么我们就把这个节点转移到节点1上并且次数加一

首先为了获取从下而上的节点序列我们进行一个拓扑排序

void topsort()
{
    queue<int> q;
    for (int i = 2; i <= n; i ++ )
        if (!d[i])q.push(i),a.pb(i);
            

    while (q.size())
    {
        auto t = q.front();
          q.pop();
        d[f[t]] --;
        if(!d[f[t]]) a.pb(f[t]),q.push(f[t]);
    }
}

然后我们进行从下往上的遍历

 for(int i = 0; i < a.size() - 1; i++)
      for(auto u : v[a[i]]) {
         if(de[u] == x) cnt ++;
         else de[a[i]] = max(de[u] + 1,de[a[i]]);
      }

然后就可以结合起来书写我们的check函数了

auto check = [&](int x) {
      int cnt = 0;
      rep(i,n) de[i] = 1;
      for(int i = 0; i < a.size() - 1; i++)
      for(auto u : v[a[i]]) {
         if(de[u] == x) cnt ++;
         else de[a[i]] = max(de[u] + 1,de[a[i]]);
      }
      return cnt <= k;
   };

本题就完成了

最后放上总代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
using namespace std;
using ll = long long;
#define int long long
#define endl '\n'
#define pb push_back
#define NO cout << "NO" << endl;
#define YES cout << "YES" << endl;
#define fi first
#define se second
#define all(x) (x).begin(),(x).end()
#define rep(i,n) for(int i = 1; i <= n; i++)
typedef pair<int,int> PII;
const int N = 2e5 + 10;
vector<int> a;
int de[N];
int d[N];
int f[N];
vector<int> v[N];
int n;
void topsort()
{
    queue<int> q;
    for (int i = 2; i <= n; i ++ )
        if (!d[i])q.push(i),a.pb(i);
            

    while (q.size())
    {
        auto t = q.front();
          q.pop();
        d[f[t]] --;
        if(!d[f[t]]) a.pb(f[t]),q.push(f[t]);
    }
}
void solve()
{
   int k; cin >> n >> k;
   rep(i,n) d[i] = 0;
   a.clear();
   rep(i,n) v[i].clear();
   for(int i = 2; i <= n; i++) {
     int x; cin >> x;
     f[i] = x;
     v[x].pb(i);
     d[x] ++;
   }
   topsort();
   
   auto check = [&](int x) {
      int cnt = 0;
      rep(i,n) de[i] = 1;
      for(int i = 0; i < a.size() - 1; i++)
      for(auto u : v[a[i]]) {
         if(de[u] == x) cnt ++;
         else de[a[i]] = max(de[u] + 1,de[a[i]]);
      }
      return cnt <= k;
   };
   int l = 1,r = 4e5;
   while( l <= r)
   {
     int mid = l + r >> 1;
     if(check(mid)) r = mid - 1;
     else l = mid + 1;
   }
   cout << l << endl;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int T;cin >> T;
    while ( T -- )
    solve();
    return 0;
}