NC13950 Alliances

发布时间 2023-06-22 01:58:36作者: 空白菌

题目链接

题目

题目描述

树国是一个有n个城市的国家,城市编号为1∼n。连接这些城市的道路网络形如一棵树,

即任意两个城市之间有恰好一条路径。城市中有k个帮派,编号为1∼k。每个帮派会占据一些城市,以进行非法交易。有时帮派之间会结盟,这就使得城市更加不安全了。同一座城市中可能有多个帮派。

当一些帮派结成联盟时,他们会更加强大,同时也更加危险。他们所控制的城市数会显著增加。具体地,一个联盟控制的城市是联盟中所有帮派所占据的城市,再加上这些城市两两之间路径上的所有城市。

shy是树国的市长,他想要选择一个城市作为首都。在决定之前,他要先做一些调研。为此,他找来你帮他回答一些询问,你能做到吗?在每个询问中,shy会选择一个城市作为首都,同时会告诉你当前活跃的帮派的集合。在这个询问中,你只需要考虑给定的集合中的帮派,其他的帮派你可以当作不存在。已知给定集合中的这些帮派结成了联盟,shy希望抓获联盟中的人,以得到关于整个联盟的一些信息。为此,他要找到被联盟控制的所有城市中离首都最近的一座城市到首都的距离。有可能首都本身就被控制了,此时答案为0。请注意,询问之间相互独立,互不影响。

输入描述

输入的第一行包含一个整数n,代表树国中的城市数。
接下来n−1行,每行包含两个整数u和v,代表城市u和v之间存在一条道路。
接下来一行包含一个整数k,代表树国中的帮派数。
接下来k行,每行描述一个帮派。第i行的第一个整数c[i]代表第i个帮派占据的城市数,接下来c[i]个整数,代表被第i个帮派占据的城市。
接下来一行包含一个整数Q,代表询问数。
接下来Q行,每行描述一个询问。每行的前两个整数V和t[i]代表本次询问中的首都与需要考虑的帮派集合的大小。接下来t[i]个整数代表本次询问中需要考虑的帮派。.

输出描述

对于每个询问,输出一行,包含一个整数,代表询问的答案。

示例1

输入

7
1 2
1 3
2 4
2 5
3 6
3 7
2
2 6 7
1 4
3
5 1 2
1 1 1
5 2 1 2

输出

2
1
1

备注

对于30%的数据,1≤n,k,Q≤1000, 1≤每个帮派占据城市数之和≤1000, 1≤每个询问中考虑的帮派数之和≤1000 对于60%的数据,1≤n,k,Q≤100000, 1≤每个帮派占据城市数之和≤100000, 1≤每个询问中考虑的帮派数之和≤100000 对于100%的数据,1≤n,k,Q≤500000, 1≤每个帮派占据城市数之和≤500000, 1≤每个询问中考虑的帮派数之和≤500000

题解

知识点:DFS序,LCA。

考虑一次询问的 \(t_i\) 个帮派的联盟,设它们城市的LCA为 \(lca\) 分类讨论:

  1. \(LCA(lca,x) \neq lca\) 时,说明 \(x\) 一定不会在这个联盟,答案是 \(dist(lca,x)\)
  2. \(LCA(lca,x) = lca\) 时,说明 \(x\) 在联盟LCA的子树里,可能会在被占据城市的路径上。此时,我们需要先得到离 \(x\) 最近的被占据城市 \(id\) ,计算 \(x\)\(LCA(x,id)\) 的距离,即 \(x\) 到最近一条路径上的城市的距离。这是因为, \(LCA(x,id)\)\(x\)\(lca\) 的路径上,因此一定在某条占据城市的路径上,且这是距离 \(x\) 最近的一个城市。

其中,关于求出某个帮派最近的一个被占据的城市,记录帮派城市的dfn序列并排序,找出在排序后的序列里,离 \(x\) dfn序左右最近的两个城市即可,可以通过二分查找。

另外,我们可以事先求出每个帮派占据城市的LCA,在多次询问中可以节省时间。

时间复杂度 \(O(n\log n+\sum c_i + \sum t_i \log c_{q_i})\)

空间复杂度 \(O(n \log n + \sum c_i)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct Graph {
    struct edge {
        int v, nxt;
    };
    int idx;
    vector<int> h;
    vector<edge> e;

    Graph(int n = 0, int m = 0) { init(n, m); }

    void init(int n, int m) {
        idx = 0;
        h.assign(n + 1, 0);
        e.assign(m + 1, {});
    }

    void add(int u, int v) {
        e[++idx] = { v,h[u] };
        h[u] = idx;
    }
};

template<class T>
struct ST {
    vector<vector<T>> node;

public:
    ST() {}
    ST(const vector<T> &src) { init(src); }

    void init(const vector<T> &src) {
        assert(src.size());
        int n = src.size() - 1;
        int sz = log2(n);
        node.assign(sz + 1, vector<T>(n + 1));
        for (int i = 1;i <= n;i++) node[0][i] = src[i];
        for (int i = 1;i <= sz;i++)
            for (int j = 1;j + (1 << i) - 1 <= n;j++)
                node[i][j] = node[i - 1][j] + node[i - 1][j + (1 << i - 1)];
    }

    T query(int l, int r) {
        int k = log2(r - l + 1);
        return node[k][l] + node[k][r - (1 << k) + 1];
    }
};

const int N = 500007;
Graph g;

int dfncnt, eulcnt;
int dfn[N], pos[N], eul[N << 1], dep[N];//! euler序列要两倍
void dfs(int u, int fa) {
    dfn[++dfncnt] = u;
    eul[++eulcnt] = u;
    pos[u] = eulcnt;
    dep[u] = dep[fa] + 1;
    for (int i = g.h[u];i;i = g.e[i].nxt) {
        int v = g.e[i].v;
        if (fa == v) continue;
        dfs(v, u);
        eul[++eulcnt] = eul[pos[u]];
    }
}

struct T {
    int mi;
    friend T operator+(const T &a, const T &b) { return { dep[a.mi] < dep[b.mi] ? a.mi : b.mi }; }
};

ST<T> st;
int LCA(int u, int v) {
    u = pos[u], v = pos[v];
    if (u > v) swap(u, v);
    return st.query(u, v).mi;
}

int dis(int u, int v) { return dep[u] + dep[v] - 2 * dep[LCA(u, v)]; }

int c[N];
int c_lca[N];
vector<int> c_id[N];
int Q[N];

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    g.init(n, n << 1);
    for (int i = 1;i <= n - 1;i++) {
        int u, v;
        cin >> u >> v;
        g.add(u, v);
        g.add(v, u);
    }

    dfs(1, 0);
    vector<T> eul_src(eulcnt + 1);
    for (int i = 1;i <= eulcnt;i++) eul_src[i] = { eul[i] };
    st.init(eul_src);

    auto f = [&](int a, int b) {return pos[a] < pos[b];};

    int k;
    cin >> k;
    for (int i = 1;i <= k;i++) {
        cin >> c[i];
        for (int j = 1;j <= c[i];j++) {
            int x;
            cin >> x;
            c_id[i].push_back(x);
            c_lca[i] = j == 1 ? x : LCA(c_lca[i], x);
        }
        sort(c_id[i].begin(), c_id[i].end(), f);
    }

    int q;
    cin >> q;
    while (q--) {
        int x, t;
        cin >> x >> t;
        int lca;
        for (int i = 1;i <= t;i++) {
            cin >> Q[i];
            lca = i == 1 ? c_lca[Q[i]] : LCA(lca, c_lca[Q[i]]);
        }
        if (LCA(x, lca) != lca) cout << dis(x, lca) << '\n';
        else {
            int ans = 1e9;
            for (int i = 1;i <= t;i++) {
                int id = lower_bound(c_id[Q[i]].begin(), c_id[Q[i]].end(), x, f) - c_id[Q[i]].begin();
                if (id < c_id[Q[i]].size())ans = min(ans, dis(x, LCA(x, c_id[Q[i]][id])));
                if (id > 0) ans = min(ans, dis(x, LCA(x, c_id[Q[i]][id - 1])));
            }
            cout << ans << '\n';
        }
    }
    return 0;
}