Loading

【题解】P6779 [Ynoi2009] rla1rmdq

奇怪的好题分享。

题意

P6779 [Ynoi2009] rla1rmdq

给定一棵包含 \(n\) 个结点的树,\(m\) 个操作以及一个长度为 \(n\) 的序列 \(a\),树边带权。

令结点 \(x\) 的深度 \(dep(x)\) 为根到 \(x\) 的路径上所有边的权值和,\(fa(x)\) 表示结点 \(x\) 的父亲。对于树根 \(rt\),有 \(fa(rt) = rt\)

对于每次操作,可以:

  1. 1 l r,表示 \(\forall l \leq i \leq r\),令 \(a_i \leftarrow fa(a_i)\)

  2. 2 l r,询问 \(\min_{i = l}^r dep(a_i)\)

\(1 \leq n, m \leq 2 \times 10^5, 1 \leq a_i \leq n\),边权取值范围为 \([0, 10^9]\)

思路

小清新分块。

这种玄学树据撅构第一反应就是奇妙根号算法,但是根号分治一类的东西感觉上没有什么前途,对颜色进行分治理论上也不太可行,于是考虑树上莫队或者分块。

注意到空间限制 64MB,然而时间限制 3s。树上莫队做这东西也不好维护,于是可以合理猜测先用玄学分块草,然后再逐块处理卡空间。根号空间复杂度可过。理论存在,实践开始。

第一眼看上去没有什么好的做法,手推一下发现不太能转化成经典问题,于是我们考虑加上一些特殊条件,看一看是否有启发。

假设全局修改,全局查询。

我们发现:假设有两个结点 \(a, b\),使得 \(b\) 在经过若干次跳跃之后到达了 \(a\) 原本所在的位置,那么 \(a\) 对答案的贡献必然包含 \(b\) 对答案的贡献,于是可以只继续令 \(a\) 向上跳。

换言之,如果某一个结点 \(u\) 向上跳跃,到达已经遍历过的结点,那么这个结点对于答案是无意义的。因此只需要每次将对答案可能有贡献的点暴力向上跳,摊下来复杂度是 \(O(n + q)\)

如果对于根号个整块,都进行一次上面的操作,那么总复杂度是 \(O((n + q) \sqrt{n})\) 的。所以对整块的修改可以直接按上面的方式暴力。

考虑对散块的修改。我们发现对于散块的修改,可能使得它重新成为所处整块中有意义的点。所以我们不妨这样考虑:对于整块修改直接先暴力跳关键点,同时记录下向上跳的步数 \(k\)。但是当修改散块的时候,我们考虑将无意义的点向上跳 \(k\) 步对齐,然后观察它是否重新获得意义。对齐一次的复杂度是 \(O(\sqrt{n} \log n)\) 的,无伤大雅。

找祖先可以考虑跳重链,单次复杂度是 \(O(\log n)\)

于是总复杂度 \(O(n \sqrt{n} \log n)\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;

const int maxn = 2e5 + 5;
const int maxm = 2e5 + 5;
const int maxe = 4e5 + 5;
const int blk_sz = 450;
const ll inf = 1e18;

struct node
{
    int to, nxt, w;
} edge[maxe];

int n, m, rt, cnt;
int a[maxn];
int que[maxn], up[maxn];
int opt[maxm], ql[maxm], qr[maxm];
int head[maxn], pos[maxn], inv[maxn], top[maxn], fa[maxn], son[maxn], sz[maxn], d[maxn];
ll dep[maxn], ans[maxm];
bool vis[maxn], in_que[maxn];

void add_edge(int u, int v, int w)
{
    cnt++;
    edge[cnt].to = v;
    edge[cnt].nxt = head[u];
    edge[cnt].w = w;
    head[u] = cnt;
}

void dfs1(int u, int f)
{
    fa[u] = f, sz[u] = 1;
    for (int i = head[u]; i; i = edge[i].nxt)
    {
        int v = edge[i].to;
        if (v != f)
        {
            dep[v] = dep[u] + edge[i].w;
            d[v] = d[u] + 1;
            dfs1(v, u);
            sz[u] += sz[v];
            if (sz[v] > sz[son[u]]) son[u] = v;
        }
    }
}

void dfs2(int u, int t)
{
    pos[u] = ++cnt;
    inv[cnt] = u;
    top[u] = t;
    if (son[u]) dfs2(son[u], t);
    for (int i = head[u]; i; i = edge[i].nxt)
    {
        int v = edge[i].to;
        if ((v != fa[u]) && (v != son[u])) dfs2(v, v);
    }
}

int kth_anc(int u, int k)
{
    if (k >= d[u]) return rt;
    while (k > d[u] - d[top[u]])
    {
        k -= (d[u] - d[top[u]] + 1);
        u = fa[top[u]];
    }
    return inv[pos[u] - k];
}

void solve(int st, int ed)
{
    int blk_cnt = 0, qlen = 0, to;
    ll res = inf;
    memset(up, 0, (n + 1) * sizeof(int));
    memset(que, 0, (n + 1) * sizeof(int));
    memset(vis, false, (n + 1) * sizeof(bool));
    memset(in_que, false, (n + 1) * sizeof(bool));
    for (int i = st; i <= ed; i++)
    {
        if (!vis[a[i]])
        {
            vis[a[i]] = in_que[i] = true;
            que[++qlen] = i;
            res = min(res, dep[a[i]]);
        }
    }
    for (int i = 1; i <= m; i++)
    {
        int l = max(ql[i], st), r = min(qr[i], ed);
        if (l > r) continue;
        if (opt[i] == 1)
        {
            if ((l == st) && (r == ed))
            {
                int tlen = qlen;
                blk_cnt++, qlen = 0;
                for (int j = 1; j <= tlen; j++)
                {
                    up[que[j]]++;
                    a[que[j]] = fa[a[que[j]]];
                    if (!vis[a[que[j]]])
                    {
                        vis[a[que[j]]] = true;
                        res = min(res, dep[a[que[j]]]);
                        que[++qlen] = que[j];
                    }
                    else in_que[que[j]] = false;
                }
            }
            else
            {
                for (int j = l; j <= r; j++)
                {
                    to = kth_anc(a[j], blk_cnt - up[j] + 1);
                    a[j] = to, up[j] = blk_cnt;
                    if (!vis[to])
                    {
                        vis[to] = true;
                        res = min(res, dep[to]);
                        if (!in_que[j]) in_que[j] = true, que[++qlen] = j;
                    }
                }
            }
        }
        else
        {
            if ((l == st) && (r == ed)) ans[i] = min(ans[i], res);
            else
            {
                for (int j = l; j <= r; j++)
                {
                    to = kth_anc(a[j], blk_cnt - up[j]);
                    ans[i] = min(ans[i], dep[to]);
                    a[j] = to, up[j] = blk_cnt;
                }
            }
        }
    }
}

int main()
{
    int st, ed;
    int u, v, w;
    scanf("%d%d%d", &n, &m, &rt);
    for (int i = 1; i <= n - 1; i++)
    {
        scanf("%d%d%d", &u, &v, &w);
        add_edge(u, v, w);
        add_edge(v, u, w);
    }
    cnt = 0;
    dfs1(rt, 0);
    dfs2(rt, rt);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= m; i++) scanf("%d%d%d", &opt[i], &ql[i], &qr[i]), ans[i] = inf;
    int len = (n + blk_sz - 1) / blk_sz;
    for (int i = 1; i <= len; i++)
    {
        st = (i - 1) * blk_sz + 1, ed = (i == len ? n : i * blk_sz);
        solve(st, ed);
    }
    for (int i = 1; i <= m; i++) 
        if (opt[i] == 2) printf("%lld\n", ans[i]);
    return 0;
}
posted @ 2022-09-29 21:50  kymru  阅读(34)  评论(0编辑  收藏  举报