AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

洛谷 CF1491H. Yuezheng Ling and Dynamic Tree    原题链接    困难

作者: 作者的头像   Conan15 ,  2024-11-27 20:25:20 ,  所有人可见 ,  阅读 128


3


题单中唯一一道黑题,但是挺套路的,类似于弹飞绵羊。

把 $a$ 序列分成 $\sqrt n$ 段,对每个数记录它第一次跳出块是跳到哪里 $pre_i$。
修改:对于零散段,暴力计算 $a$,暴力把整段重构;对于大块,直接打个标记记录整体要减多少,再根据情况看是否要暴力重构。
观察到一个段只要减去了 $\geq \sqrt n$ 次就一定有 $pre_i = a_i$,所以此时就不需要暴力重构。

查询就类似重链剖分往上跳。

这里放的是 双倍经验 的代码。

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

template<typename T>
void read(T &x){
    x=0;int f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    x*=f;
}
template<typename T,typename ... Args>
void read(T &x, Args &... y){read(x);read(y...);}

const int N = 4e5 + 15;
int n, q, B, a[N], pre[N];
int pos[N];

int m, l[N], r[N], flag[N], cnt[N];

inline int del(int x, int d) { return max(x - d, 1); }
inline void init() {
    for (int i = 1; i * B <= n; i++) ++m, l[m] = r[m - 1] + 1, r[m] = i * B;
    if (m * B < n) ++m, l[m] = r[m - 1] + 1, r[m] = n;

    for (int i = 1; i <= m; i++)
        for (int j = l[i]; j <= r[i]; j++) pos[j] = i;
}
inline void build(int u) {
    if (flag[u])
        for (int i = l[u]; i <= r[u]; i++) a[i] = del(a[i], flag[u]);
    flag[u] = 0;
    for (int i = l[u]; i <= r[u]; i++) pre[i] = (a[i] < l[u]) ? a[i] : pre[a[i]];
}

inline void change(int x, int y, int d) {
    if (pos[x] == pos[y]) {
        for (int i = x; i <= y; i++) a[i] = del(a[i], d);
        build(pos[x]);
        return;
    }
    for (int i = x; i <= r[pos[x]]; i++) a[i] = del(a[i], d);
    for (int i = l[pos[y]]; i <= y; i++) a[i] = del(a[i], d);
    build(pos[x]), build(pos[y]);

    for (int u = pos[x] + 1; u <= pos[y] - 1; u++) {
        flag[u] = min(flag[u] + d, n);
        if (cnt[u] <= B) build(u);  //更新次数较少,暴力重构
        cnt[u] = min(cnt[u] + d, n);
    }
}

inline int lca(int x, int y) {
    while (1) {
        if (pos[x] < pos[y]) swap(x, y);
        int fx = del(pre[x], flag[pos[x]]), fy = del(pre[y], flag[pos[y]]);
        if (pos[x] > pos[y]) x = fx;
        else if (fx != fy) x = fx, y = fy;
        else break;
    }
    while (x != y) {
        if (x < y) swap(x, y);
        x = del(a[x], flag[pos[x]]);
    }
    return x;
}

int main() {
    read(n, q), B = 500;
    init();
    for (int i = 2; i <= n; i++) read(a[i]);
    for (int i = 1; i <= m; i++) build(i);
    int lst = 0;
    while (q--) {
        int opt; read(opt);
        if (opt == 1) {
            int l, r, x; read(l, r, x);
            l ^= lst, r ^= lst, x ^= lst;
            change(l, r, x);
        } else {
            int u, v; read(u, v);
            u ^= lst, v ^= lst;
            lst = lca(u, v);
            printf("%d\n", lst);
        }
    }
    return 0;
}

2 评论


用户头像
奶龙_czj   2024-11-28 07:41         踩      回复

我这种垃圾都只能看题解做出黑题,呜呜呜呜呜


用户头像
奶龙_czj   2024-11-28 07:40         踩      回复

套路题,大神啊,膜拜你


App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息