第32次CCF计算机软件能力认证 树上搜索题解

树上搜索题解

题目链接

传送门

题目描述

给一棵 n n n 个结点的树,每个结点的权值为 w [ i ] w[i] w[i], 给出 m m m 个询问,每次询问给出一个结点 u u u,进行如下操作:

  • 找树的重心
  • 询问 u u u 是否是该点的子孙结点,如果是的话则删除这个点所构成的子树之外的所有点,否则删除该点所构成的子树所有点
  • 最后只剩下结点 u u u,则结束
    求删除点的序列

思路分析

由于每次是删除重心,也就是最靠近当前树的结点权值的平均数的点,所以每次结点权值减少大概为 1 2 \frac{1}{2} 21,所以大概是要删 l o g n logn logn 个点,所以可以考虑暴力,每次模拟删除过程即可。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long 
typedef pair<int, int>PII;
#define x first
#define y second
void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> w(n + 1);
    vector<int> f(n + 1);
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
        sum += w[i];
    }
    vector<vector<int>> g(n + 1);
    for (int i = 2; i <= n; i++) {
        int p;
        cin >> p;
        f[i] = p;
        g[p].emplace_back(i);
    }
    auto dfs = [&](auto &self, int u, int fa) -> void {
        for (auto v : g[u]) {
            self(self, v, u);
            w[u] += w[v];
        }
    };
    dfs(dfs, 1, -1);
    vector<int> ww;
    ww = w;
    int rsum = sum;
    for (int t = 1; t <= m; t++) {
        int x;
        cin >> x;
        sum = rsum;
        w = ww;
        vector<int> st(n + 1, 0);
        for (int j = 1; j <= n; j++) {
            int s = sum;
            int p = 0;
            for (int i = 1; i <= n; i++) {
                if(abs(2 * w[i] - sum) < s && !st[i]) {
                    s = abs(2 * w[i] - sum);
                    p = i;
                }
            }
            bool flag = false;
            auto dfs_ = [&](auto &self, int u, int fa) -> void {
                if(x == u)flag = true;
                for (auto v : g[u]) {
                    self(self, v, u);
                }
            };
            dfs_(dfs_, p, -1);
            auto dfs_1 = [&](auto &self, int u, int k) -> void {
                // cerr << u << " " << st[u] << "\n";
                if(!k)st[u] -= 1;
                else st[u] = 1;
                // cerr << u << " " << st[u] << "\n";
                for (auto v : g[u]) {
                    self(self, v, k);
                }
            };
            if(flag == true) {
                sum = w[p];
                for (int i = 1; i <= n; i++) {
                    st[i] += 1;
                }
                dfs_1(dfs_1, p, 0);
            } else {
                sum -= w[p];
                dfs_1(dfs_1, p, 1);
                int x = p, v = w[p];
                while(x != 0) {
                    w[x] -= v;
                    x = f[x];
                }
            }
            int cnt = 0;
            // cerr << p << "\n";
            for (int i = 1; i <= n; i++) {
                if(st[i] == 0)cnt++;
                // cerr << i << "! " << st[i] << "\n";
            }
            cout << p << " ";
            if(cnt == 1) break;
        }
        cout << "\n";
    }
}
signed main() {
   ios::sync_with_stdio(false);
   cin.tie(0), cout.tie(0);
   //cout << fixed << setprecision(10);
   //init();
   
   int T = 1;
   //cin >> T;
   while(T --) solve();
   
   return 0;
}
  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值