CF1320E Treeland and Viruses

发布时间 2023-12-30 21:01:13作者: Hanx16Msgr

Treeland and Viruses

Luogu CF1320E

题面翻译

有一棵有 \(n\) 个节点的树,\(q\) 次询问(询问互相独立),每次给定 \(k_i\) 个颜色,每个颜色有一个起始点 \(v_j\) 和移动速度 \(s_j\),每一个颜色在每一次操作中会使它周围没有被染色的连通块上与它的距离不超过 \(s_j\) 的点全部染为这一个颜色,每一轮中,颜色从 \(1\)\(k_i\) 依次开始操作,一直到所有点全部被染色为止,再询问 \(m_i\) 个关键点的颜色。

Solution

标的 *3000,体感 *2300,因为我会做?(虽然套路)。

考虑暴力怎么做。不难想到一种每次询问都是 \(\mathcal O(n)\) 的暴力:直接 BFS,开始的时候将所有的起始点加入队列,然后按顺序取出并扩展到周围节点,然后再确定关键点的颜色。因为每条边的边权都是 \(1\),所以复杂度不难做到 \(\mathcal O(n)\),总时间复杂度 \(\mathcal O(nq)\),不是很能过。

注意到题目保证了询问的 \(\sum k\le 2\times10^5\),并且每次询问的时候我们会多处理很多的无用点,即仅出现在关键点和起点路径上的点,这些点的颜色其实我们并不关心,所以套路地想到使用虚树来减少每次询问点的数量。这样每次询问的点数变成了 \(\mathcal O(k)\)。考虑继续上面暴力的做法,发现现在的边权并非是 \(1\),所以使用 Dijstra 对点进行拓展即可,时间复杂度可以做到 \(\mathcal O(n\log n)\)

代码实现有一些细节需要注意,总时间复杂度 \(\mathcal O(\sum k\log n)\),常数超大。

Code
int N, Q, K, M;
namespace RealTree {
    vector<int> e[_N];
    int siz[_N], son[_N], dep[_N], fa[_N], top[_N], dfn[_N], tot;
    void addEdge(int x, int y) {e[x].epb(y), e[y].epb(x);}
    void dfs1(int x, int F) {
        fa[x] = F, dep[x] = dep[F] + 1, siz[x] = 1;
        for (int v: e[x]) if (v ^ F) {
            dfs1(v, x), siz[x] += siz[v];
            if (siz[v] > siz[son[x]]) son[x] = v;
        }
    }
    void dfs2(int x, int topf) {
        top[x] = topf, dfn[x] = ++tot;
        if (son[x]) dfs2(son[x], topf);
        for (int v: e[x]) if (v ^ son[x] && v ^ fa[x])
            dfs2(v, v);
    }
    inline void init() {dfs1(1, 0), dfs2(1, 1);}
    int getLca(int x, int y) {
        while (top[x] != top[y]) {
            if (dep[top[x]] < dep[top[y]]) swap(x, y);
            x = fa[top[x]];
        }
        return dep[x] < dep[y] ? x : y;
    }
    int getDis(int x, int y) {return dep[x] + dep[y] - 2 * dep[getLca(x, y)];}
}
namespace VirtTree {
    vector<int> e[_N];
    vector<int> pset;
    void addEdge(int x, int y) {e[x].epb(y), e[y].epb(x);}
    void build(vector<int> p) {
        auto cmp = [&](const auto &i, const auto &j) {
            return RealTree::dfn[i] < RealTree::dfn[j];
        };
        sort(All(p), cmp);
        vector<int> vec = p;
        For(i, 1, p.size() - 1)
            vec.epb(RealTree::getLca(p[i], p[i-1]));
        sort(All(vec), cmp);
        vec.resize(unique(All(vec)) - vec.begin());
        for (int x: vec) e[x].clear();
        For(i, 1, vec.size() - 1) {
            int lca = RealTree::getLca(vec[i-1], vec[i]);
            addEdge(lca, vec[i]);
        }
        pset.swap(vec);
    }
}
vector<pii> crl;
vector<int> qlist;
int ans[_N], id[_N];
pii dist[_N];
bool vis[_N];
void init() {
    for (int x: VirtTree::pset) id[x] = 0;
    For(i, 0, M - 1) id[qlist[i]] = i + 1;
    for (int x: VirtTree::pset) {
        dist[x] = mkp(inf, inf);
        vis[x] = 0;
    }
}
void Solve() {
    using VirtTree::e, RealTree::getDis;
    init();
    priority_queue<pair<pii, int>> q;
    For(i, 0, K - 1) {
        auto [x, s] = crl[i];
        q.emplace(mkp(0, -i - 1), x);
        dist[x] = mkp(0, i + 1), ans[id[x]] = i + 1;
    }
    while (!q.empty()) {
        auto cur = q.top(); q.pop();
        if (vis[cur.se]) continue;
        vis[cur.se] = 1;
        for (int v: e[cur.se]) {
            int d = getDis(crl[-cur.fi.se-1].fi, v);
            int t = ceil(d * 1. / crl[-cur.fi.se-1].se);
            if (dist[v] > mkp(t, -cur.fi.se)) {
                dist[v] = mkp(t, -cur.fi.se);
                ans[id[v]] = -cur.fi.se;
                q.emplace(mkp(-t, cur.fi.se), v);
            }
        }
    }
}
void _() {
    cin >> N;
    For(i, 2, N) {
        int x, y; cin >> x >> y;
        RealTree::addEdge(x, y);
    }
    RealTree::init();
    cin >> Q;
    while (Q--) {
        cin >> K >> M;
        crl.resize(K), qlist.resize(M);
        vector<int> p;
        for (auto &pr: crl) cin >> pr.fi >> pr.se, p.epb(pr.fi);
        for (int &x: qlist) cin >> x, p.epb(x);
        VirtTree::build(p);
        Solve();
        For(i, 1, M) fmtout("{} ", ans[i]);
        fmtout("\n");
    }
}