Comet OJ - Contest #9 & X Round 3 【XR-3】核心城市(树的直径,树的中心)

题目链接

题意:就是在一棵树里面找一个k个节点的联通块,使不在联通块里的节点到联通块里节点的最大距离最小,这里有一道基本一样的题,但是题意写的比较清楚可以先看看Power oj 2853 小Z的糖果店

题解:这个题首先要求给出树的中心(树的直径上的中间那个点)。为什么要求这个点呢?因为这个点到其他叶子结点(离它距离最远的节点)的距离较为平均,这样就尽量使每个点到这个点的距离尽量的小,这就满足了第一个条件(最远距离最小)。但是我们现在需要找的是一个联通块不是一个点,处理这个问题,我们提前处理出各点的子节点到该点父节点距离的最大值。然后我们从中心点开始(保证中心点在这个联通块中),然后删除k个距离最大的点,剩下最大的就是我们要求的值(这步可以用优先队列处理)。

#pragma comment(linker, "/STACK:102400000,102400000")

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>

const int mod = 998244353;
const int maxn = 2e5 + 5;
const int inf = 1e9;
const long long onf = 1e18;
#define me(a, b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI 3.14159265358979323846
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int head[maxn], tot;
int dis[maxn], father[maxn];
int st, ed, tempmin;
bool vis[maxn];
struct node {
    int v, next;
} tree[maxn];

struct Node {
    int pos, dis;

    bool friend operator<(Node a, Node b) {
        return a.dis < b.dis;
    }
};

void add_edge(int u, int v) {
    tree[tot] = node{v, head[u]};
    head[u] = tot++;
}

void init() {
    me(head, -1);
    tot = 0;
}

void dfs(int u, int fa) {
    if (dis[u] > tempmin)
        tempmin = dis[u], ed = u;
    for (int i = head[u]; i != -1; i = tree[i].next) {
        int v = tree[i].v;
        if (fa == v)
            continue;
        father[v] = u;
        dis[v] = dis[u] + 1;
        dfs(v, u);
    }
}

int dfslen(int u, int fa) {
    int ans = 0;
    for (int i = head[u]; i != -1; i = tree[i].next) {
        int v = tree[i].v;
        if (fa == v)
            continue;
        int temp = dfslen(v, u) + 1;
        ans = max(ans, temp);
        dis[v] = temp;
    }
    return ans;
}

int get_root() {
    st = 1, dis[1] = 0, tempmin = 0;
    dfs(st, -1);
    dis[ed] = 0, tempmin = 0, st = ed, father[ed] = -1;
    dfs(st, -1);
    int root, nowpos = ed, temp = inf;
    while (nowpos != -1) {
        //   cout<<dis[nowpos]<<" "<<dis[ed]-dis[nowpos]<<endl;
        int Max = max(dis[nowpos], dis[ed] - dis[nowpos]);
        if (Max < temp)
            root = nowpos, temp = Max;
        nowpos = father[nowpos];
    }
    return root;
}

int main() {
   // freopen("1.in", "r", stdin);
    //  freopen("1.out", "w", stdout);
    int n, k;
    cin >> n >> k;
    init();
    for (int i = 1; i < n; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        add_edge(u, v);
        add_edge(v, u);
    }
    int root = get_root();///找树的中心
    dfslen(root, -1);///处理出各点的子节点到该点的父节点的距离
    priority_queue<Node> q;
    q.push(Node{root, dis[root]});
    vis[root] = true;
    for (int i = 1; i <= k; i++) {
        Node now = q.top();
        q.pop();
        for (int j = head[now.pos]; j != -1; j = tree[j].next) {
            int v = tree[j].v;
            if (!vis[v]) {
                vis[v] = true;
                q.push(Node{v, dis[v]});
            }
        }
    }
    if (q.empty())
        puts("0");
    else
        printf("%d\n", q.top().dis);
    return 0;
}

 

©️2020 CSDN 皮肤主题: 创作都市 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值