NC13950 Alliances(DFS序 + LCA)

题目链接

题意:
一 个 国 家 有 n 个 城 市 , 形 成 一 棵 树 , 有 n − 1 条 边 一个国家有n个城市,形成一棵树,有n-1条边 nn1
国 家 中 有 k 个 帮 派 , 分 别 占 领 一 些 城 市 国家中有k个帮派,分别占领一些城市 k
每 个 帮 派 占 领 c i 个 城 市 , 以 及 这 c i 个 城 市 路 径 上 的 所 有 点 每个帮派占领c_i个城市,以及这c_i个城市路径上的所有点 cici
帮 派 可 以 联 盟 , 联 盟 会 将 几 个 帮 派 的 所 有 占 领 城 市 和 路 径 上 的 城 市 占 领 帮派可以联盟,联盟会将几个帮派的所有占领城市和路径上的城市占领
q 次 询 问 , 首 都 如 果 建 在 v 城 市 q次询问,首都如果建在v城市 qv
求 首 都 离 给 定 t i 个 帮 派 联 盟 中 最 近 占 领 城 市 的 距 离 求首都离给定t_i个帮派联盟中最近占领城市的距离 ti
题解:
由 于 这 是 一 棵 树 , 路 径 只 有 一 条 , 那 么 就 假 设 根 为 1 由于这是一棵树,路径只有一条,那么就假设根为1 1
那 么 就 可 以 用 L C A 找 到 这 些 帮 派 占 领 的 最 高 的 父 亲 节 点 那么就可以用LCA找到这些帮派占领的最高的父亲节点 LCA
求 出 每 个 帮 派 的 L C A 后 , 联 盟 的 时 候 , 找 这 几 个 帮 派 的 L C A 求出每个帮派的LCA后,联盟的时候,找这几个帮派的LCA LCALCA
如 果 发 现 首 都 不 在 这 个 L C A 的 子 树 中 , 那 肯 定 最 短 距 离 就 是 到 这 个 L C A 的 距 离 如果发现首都不在这个LCA的子树中,那肯定最短距离就是到这个LCA的距离 LCALCA
树 上 两 点 距 离 可 以 用 两 点 的 深 度 和 减 去 2 倍 两 点 L C A 的 深 度 求 解 树上两点距离可以用两点的深度和减去2倍两点LCA的深度求解 2LCA

然 后 就 要 考 虑 如 果 首 都 建 在 这 个 子 树 里 然后就要考虑如果首都建在这个子树里
子 树 里 可 能 刚 好 在 联 盟 占 领 的 城 市 里 , 或 者 不 在 子树里可能刚好在联盟占领的城市里,或者不在
由 于 每 次 询 问 给 出 的 联 盟 帮 派 个 数 不 是 很 多 由于每次询问给出的联盟帮派个数不是很多
所 以 直 接 暴 力 枚 举 这 些 联 盟 帮 派 , 找 最 近 点 所以直接暴力枚举这些联盟帮派,找最近点
这 时 候 就 要 用 到 d f s 序 这时候就要用到dfs序 dfs
利 用 二 分 在 每 个 帮 派 占 领 的 城 市 中 , 找 到 距 离 首 都 城 市 最 近 的 两 个 城 市 利用二分在每个帮派占领的城市中,找到距离首都城市最近的两个城市
也 就 是 第 一 个 大 于 等 于 他 , 和 第 一 个 小 于 他 的 城 市 也就是第一个大于等于他,和第一个小于他的城市
( 因 为 找 的 是 大 于 等 于 , 如 果 有 等 于 的 情 况 就 说 明 首 都 就 是 被 占 领 点 , 结 果 是 0 ) (因为找的是大于等于,如果有等于的情况就说明首都就是被占领点,结果是0) (0)
由 于 首 都 是 在 联 盟 的 L C A 的 子 树 中 , 所 以 找 到 的 这 两 个 被 占 领 的 城 市 由于首都是在联盟的LCA的子树中,所以找到的这两个被占领的城市 LCA
到 他 们 父 亲 结 点 的 路 径 一 定 全 部 都 是 被 占 领 的 到他们父亲结点的路径一定全部都是被占领的
所 以 想 要 找 最 近 的 , 就 应 该 找 到 首 都 和 他 们 的 L C A 所以想要找最近的,就应该找到首都和他们的LCA LCA
在这里插入图片描述
这 个 图 中 , 假 设 黑 色 是 被 占 领 点 , 如 果 1 和 4 被 占 领 这个图中,假设黑色是被占领点,如果1和4被占领 14
1 就 是 这 个 联 盟 占 领 的 最 高 点 , 将 在 6 建 首 都 , 6 在 最 高 点 的 子 树 中 1就是这个联盟占领的最高点,将在6建首都,6在最高点的子树中 166
标 注 的 123456 代 表 的 就 是 d f s 序 , 6 相 邻 最 近 被 占 领 的 点 是 4 标注的123456代表的就是dfs序,6相邻最近被占领的点是4 123456dfs64
但 是 4 到 6 的 距 离 明 显 没 有 1 到 6 近 , 所 以 要 进 行 对 4 和 6 求 L C A 但是4到6的距离明显没有1到6近,所以要进行对4和6求LCA 461646LCA
然 后 再 计 算 这 个 L C A 到 6 的 距 离 才 是 最 近 的 然后再计算这个LCA到6的距离才是最近的 LCA6

找 第 一 个 大 于 等 于 首 都 d f s 序 的 点 也 是 类 似 道 理 找第一个大于等于首都dfs序的点也是类似道理 dfs
这 个 图 没 有 表 示 全 部 情 况 , 只 举 例 说 了 一 下 找 第 一 个 小 于 的 这个图没有表示全部情况,只举例说了一下找第一个小于的
然 后 在 这 两 个 点 都 能 取 到 的 情 况 下 取 最 小 结 果 , 就 是 最 终 答 案 然后在这两个点都能取到的情况下取最小结果,就是最终答案

AC代码

/*
    Author : zzugzx
    Lang : C++
    Blog : blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod = 1e9+7;
//const int mod = 998244353;
const double eps = 1e-10;
const double pi = acos(-1.0);
const int maxn = 1e6+10;
const ll inf = 0x3f3f3f3f;
const int dir[][2]={{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};

int n,depth[maxn], f[maxn][50];
int from[maxn], to[maxn << 1], nxt[maxn << 1], cnt, Log[maxn];
int L[maxn], R[maxn], id, pos[maxn], top[maxn];
void addEdge (int u, int v) {
    to[++cnt] = v, nxt[cnt] = from[u], from[u] = cnt;
}
void dfs (int u, int fa) {
    depth[u] = depth[fa] + 1;
    L[u] = ++id;
    pos[id] = u;
    for (register int i = 1; i <= Log[n]; ++i) {
        if ((1 << i) > depth[u]) break;
        f[u][i] =  f[f[u][i - 1]][i - 1];
    }
    for (register int i = from[u]; i; i = nxt[i]) {
        ll v = to[i];
        if (v == fa) continue;
        f[v][0] = u;
        dfs (v, u);
    }
    R[u] = id;
}
inline int LCA (int x, int y) {
    if (depth[x] < depth[y]) swap(x, y);
    for(register int i = Log[n] ; i >= 0 ; --i)
		if(depth[x] - (1 << i) >= depth[y]) x = f[x][i];
    if (x == y) return x;
    for (register int i = Log[n]; i >= 0; --i)
        if (f[x][i] != f[y][i])
            x = f[x][i], y = f[y][i];
    return f[x][0];
}
void init(){
	Log[0] = -1;
    for (register int i = 1, u, v; i < n; ++i) {
        cin >> u >> v;
        addEdge (u, v); addEdge(v, u);
        Log[i] = Log[i >> 1] + 1;
    }
    Log[n] = Log[n >> 1] + 1;
    dfs(1,0);
}
int dis(int p , int q){return depth[p] + depth[q] - 2 * depth[LCA(p , q)];}
vector<int> g[maxn];
int a[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
//  freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    cin >> n;
    init();
    int q;
    cin >> q;
    for (int i = 1; i <= q; i++) {
        int k;
        cin >> k;
        for(int j = 1; j <= k; j++) {
            int x;
            cin >> x;
            g[i].pb(L[x]);
            if (j == 1) top[i] = x;
            else top[i] = LCA(top[i], x);
        }
        sort(all(g[i]));
    }
    cin >> q;
    while (q--) {
        int v, t;
        cin >> v >> t;
        int rt;
        for (int i = 1; i <= t; i++){
            cin >> a[i];
            if (i == 1) rt = top[a[i]];
            else rt = LCA(rt, top[a[i]]);
        }
        if (L[v] < L[rt] || L[v] > R[rt]) {
            cout << dis(v, rt) << endl;
            continue;
        }
        int ans = inf;
        for (int i = 1; i <= t; i++){
            auto p = lower_bound(all(g[a[i]]), L[v]);
            if (p != g[a[i]].end())
                ans = min(ans, dis(v, LCA(v, pos[*p])));
            if (p !=g[a[i]].begin())
                ans = min(ans, dis(v, LCA(v, pos[*prev(p)])));
        }
        cout << ans << endl;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值