首页 > 其他分享 >杭电23多校第九场Capoo on tree(二分+树链剖分+可持久化线段树)

杭电23多校第九场Capoo on tree(二分+树链剖分+可持久化线段树)

时间:2023-08-16 22:56:47浏览次数:39  
标签:pre suf 剖分 23 int top tree len Info

2023HDU多校9__Capoo on tree(二分+树链剖分+可持久化线段树)

题目链接

Solution

\(Hint1\) 考虑如何进行对某一相同点权的所有点进行点权\(+1\)操作,如果我们建立权值线段树,那只需要将权值为\(x\)的点并入权值\(x+1\)即可,但是这样就算我们建立以节点编号为版本的可持久化线段树,也是无法处理询问的,因为题目的询问是问一条路径上的一个连续子段,那想要完成这个询问就只能枚举路径上每一个节点,去查看每一段长为\(l\)的路径中是否只出现了\(\ge y\)的权值,复杂度是不允许的,并且想要找到这条路径的另一个端点也很困难。那么我们不妨试试以权值建立版本,那一般来说我们会从小到大建立版本,那么此时的版本为\(val\)的线段树存储的就是所有\(\le val\)的节点信息,由于询问的是\(\ge y\)的信息,所以我们直接从大到小建立版本,省得再做一次差分,这样子我们的版本存储的就是所有\(\ge val\)的节点信息。那么此时我们的\(+1\)操作就相当于把版本为\(x\)的信息替换\(x+1\),那么同理\(-1\)操作就相当于把\(x+1\)的版本信息替换\(x\),由于是路径上的操作,所以要树剖后进行区间更新,把对应的区间版本进行替换就行。

\(Hint2\) 考虑如何求答案,由于我们现在有了\(\ge y\)的节点信息,我们考虑维护什么样的信息能够得到\(u\)和路径上最早出现连续一段\(\ge y\)且长度至少为\(l\)的子段之间的距离,如果我们能够找到起点,那直接求一下\(lca\)就可以得到距离,那经典的题目有维护最大子段和,我们可以类似的通过一个前缀和后缀以及长度来维护出一个长度至少为\(l\)的最大子段。信息按照如下进行合并即可

Info operator+(const Info &x, const Info &y) {
    Info res;
    res.pre = (x.pre == x.len ? x.pre + y.pre : x.pre);
    res.suf = (y.suf == y.len ? x.suf + y.suf : y.suf);
    res.Max = max({x.Max, y.Max, x.suf + y.pre});
    res.len = x.len + y.len;
    return res;
}

通过给出的一条路径我们会得到若干条链,我们考虑把这些链全都处理出来,那只要按照经典的树链剖分去做就好了,但是我们需要的路径是从\(u\rightarrow lca\)的路径和从\(lca \rightarrow u\), 上面的\(dfn\)序可能并不单调递增或递减的,需要讨论清楚(具体参考代码)。我们处理出这些链之后我们遍历这几条链来进行处理询问,有两种情况。一种是前面链的后缀和当前链的前缀进行拼接,还有一种是在某一条链的内部。对于第一种情况我们可以直接利用\(dfn\)的连续性做差得到目标点,第二种情况我的做法是二分终点再做差得到目标点,可以把二分放到线段树上但我不会QAQ。

复杂度\(O(n\log^2{n})\)(极其简单的主函数,纯数据结构,除了版本处理的思维,剩下的都是对基本功的考验QAQ)

Code

int n, m, a[maxn];
vector<int> tmp[maxn], e[maxn];

int dfn, in[maxn], fa[maxn], son[maxn], sz[maxn], dep[maxn], top[maxn], id[maxn];
void dfs(int u, int p) {
    sz[u] = 1;
    fa[u] = p;
    dep[u] = dep[p] + 1;
    for (auto v : e[u]) if (v != p) {
        dfs(v, u);
        sz[u] += sz[v];
        if (!son[u] || sz[son[u]] < sz[v]) son[u] = v;
    }
}

void DFS(int u, int p) {
    top[u] = p;
    in[u] = ++dfn;
    id[dfn] = u;
    if (son[u]) DFS(son[u], p);
    for (auto v : e[u])
        if (v != fa[u] && v != son[u])
            DFS(v, v);
}
int lca(int u, int v) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        u = fa[top[u]];
    }
    return u < v ? u : v;
}
struct Info {
    int pre, suf, Max, len;
    Info() { pre = suf = Max = len = 0; }
    Info(int _pre, int _suf, int _Max, int _len) : pre(_pre), suf(_suf), Max(_Max), len(_len) {}
};
Info operator+(const Info &x, const Info &y) {
    Info res;
    res.pre = (x.pre == x.len ? x.pre + y.pre : x.pre);
    res.suf = (y.suf == y.len ? x.suf + y.suf : y.suf);
    res.Max = max({x.Max, y.Max, x.suf + y.pre});
    res.len = x.len + y.len;
    return res;
}
Info sum[maxn << 5];
int ls[maxn << 5], rs[maxn << 5], root[maxn << 5], idx;

void clear() {
    for (int i = 0; i <= n + 1; ++i) {
        son[i] = 0;
        root[i] = 0;
        e[i].clear();
        tmp[i].clear();
    }
    for (int i = 0; i <= idx; ++i) {
        sum[i] = Info();
        ls[i] = rs[i] = 0;
    }
    idx = dfn = 0;
}
void build(int &rt, int l, int r) {
    rt = ++idx;
    sum[rt] = Info(0, 0, 0, r - l + 1);
    if (l == r) return ;
    int mid = l + r >> 1;
    build(ls[rt], l, mid); build(rs[rt], mid + 1, r);
}

void insert(int rtu, int &rtv, int l, int r, int pos) {
    rtv = ++idx;
    ls[rtv] = ls[rtu], rs[rtv] = rs[rtu], sum[rtv] = sum[rtu];
    if (l == r) {
        sum[rtv] = Info(1, 1, 1, 1);
        return ;
    }
    int mid = l + r >> 1;
    if (pos <= mid) insert(ls[rtu], ls[rtv], l, mid, pos);
    else insert(rs[rtu], rs[rtv], mid + 1, r, pos);
    sum[rtv] = sum[ls[rtv]] + sum[rs[rtv]];
}


void linkCopy(int &rtu, int rtv, int l, int r, int ql, int qr) {
    int p = rtu;
    rtu = ++idx;
    sum[rtu] = sum[p], ls[rtu] = ls[p], rs[rtu] = rs[p];
    if (ql <= l && r <= qr) {
        sum[rtu] = sum[rtv];
        ls[rtu] = ls[rtv];
        rs[rtu] = rs[rtv];
        return ;
    }
    int mid = l + r >> 1;
    if (ql <= mid) linkCopy(ls[rtu], ls[rtv], l, mid, ql, qr);
    if (mid < qr) linkCopy(rs[rtu], rs[rtv], mid + 1, r, ql, qr);
    sum[rtu] = sum[ls[rtu]] + sum[rs[rtu]];
}

void Copy(int u, int v, int rtu, int rtv) {
    while (top[u] != top[v]) {
        if (dep[top[u]] < dep[top[v]]) swap(u, v);
        linkCopy(root[rtu], root[rtv], 1, n, in[top[u]], in[u]);
        u = fa[top[u]];
    }
    if (dep[u] > dep[v]) swap(u, v);
    linkCopy(root[rtu], root[rtv], 1, n, in[u], in[v]);
}

Info linkQuery(int rt, int l, int r, int ql, int qr) {
    if (ql <= l && r <= qr) return sum[rt];
    int mid = l + r >> 1;
    Info Ans;
    if (ql <= mid) Ans = Ans + linkQuery(ls[rt], l, mid, ql, qr);
    if (mid < qr) Ans = Ans + linkQuery(rs[rt], mid + 1, r, ql, qr);
    return Ans;
}


void Query(int u, int v, int rt, int l) {
    int U = u;
    vector<pii> link, revlink;
    while(top[u] != top[v]) {
        if (dep[top[u]] > dep[top[v]]) {
            link.emplace_back(in[u], in[top[u]]);
            u = fa[top[u]];
        } else {
            revlink.emplace_back(in[top[v]], in[v]);
            v = fa[top[v]];
        }
    }
    link.pb(mkp(in[u], in[v]));
    for (int i = SZ(revlink) - 1; i >= 0; --i) link.emplace_back(revlink[i]);
    Info Ans;
    int pos = 0;
    vector<pii> preSeg;
    for (auto i : link) {
        auto [x, y] = i;
        Info now = x < y ? linkQuery(root[rt], 1, n, x, y) : linkQuery(root[rt], 1, n, y, x);
        if (x > y) swap(now.pre, now.suf);
         if (Ans.suf + now.pre >= l && Ans.suf) {
            for (int j = SZ(preSeg) - 1; j >= 0; --j) {
                int len = abs(preSeg[j].fir - preSeg[j].sec) + 1;
                if (Ans.suf > len) Ans.suf -= len;
                else {
                    auto [X, Y] = preSeg[j];
                    if (X < Y) pos = id[Y - Ans.suf + 1];
                    else pos = id[Y + Ans.suf - 1];
                    break;
                }
            }
            break;
        }
        Ans = Ans + now;
        preSeg.emplace_back(i);
        if (now.Max >= l) {
            if (x < y) {
                int L = x, R = y;
                while (L <= R) {
                    int mid = L + R >> 1;
                    if (linkQuery(root[rt], 1, n, x, mid).Max >= l) R = mid - 1;
                    else L = mid + 1;
                }
                pos = id[R + 1 - l + 1];
            } else {
                int L = y, R = x;
                while (L <= R) {
                    int mid = L + R >> 1;
                    if (linkQuery(root[rt], 1, n, mid, x).Max >= l) L = mid + 1;
                    else R = mid - 1;
                }
                pos = id[L - 1 + l - 1];
            }
            break;
        }
    }
    cout << (pos ? dep[U] + dep[pos] - 2 * dep[lca(U, pos)] : -1) << '\n';
}

void solve() {
    cin >> n >> m;
    clear();
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        tmp[a[i]].push_back(i);
    }
    for (int i = 1; i < n; ++i) {
        int u, v; cin >> u >> v;
        e[u].emplace_back(v); e[v].emplace_back(u);
    }
    dfs(1, 0); DFS(1, 1);
    build(root[n + 1], 1, n);
    for (int i = n; i >= 1; --i) {
        root[i] = root[i + 1];
        for (auto j : tmp[i])
            insert(root[i], root[i], 1, n, in[j]);
    }
    for (int i = 1; i <= m; ++i) {
        int op; cin >> op;
        if (op == 1) {
            int u, v, x; cin >> u >> v >> x;
            Copy(u, v, x + 1, x);
        } else if (op == 2) {
            int u, v, x; cin >> u >> v >> x;
            Copy(u, v, x, x + 1);
        } else {
            int u, v, l, y; cin >> u >> v >> l >> y;
            Query(u, v, y, l);
        }
    }
}

标签:pre,suf,剖分,23,int,top,tree,len,Info
From: https://www.cnblogs.com/Fighoh/p/17636424.html

相关文章

  • 2023.8.16模拟赛总结
    T1Idiot的乘幂题目大意就是给\(a,b,c,d,p\)满足求解这题考场一开始发现\(\gcd(a,c)=1\)没啥用,后来发现其实很巧妙,直接辗转相除\(a,c\)同时维护\(\chi^a,\chi^c\)最后剩下来的就是\(\chi\)当然题解给了一个鬼才想到的做法构造\(\chi=\chi^1=\chi^{ax+cy}\)所以可以用exgc......
  • 2023.8.16
    今天没做什么,主要是栈溢出差不多学完了,想花点时间再把基础打好一点今天去b站找到个pwn的视频,最近打算去看一下其中有关gdb调试的相关东西,恰好我调试相关的东西只会一点最简单的,想更好地去做pwn题,感觉这方面还是要学好。中间应该也会去找一些题目来做考虑到九月有竞赛,我在此之前......
  • 20230816比赛
    T1矩形Description现在我们在一个平面上画了n个矩形。每一个矩形的两边都与坐标轴相平行,且矩形定点的坐标均为整数。现我们定义满足如下性质的图形为一个块:每一个矩形都是一个块;如果两个块有一段公共的部分,那么这两个块就会形成一个新的块,否则这两个块就是不同的。示......
  • 「Log」2023.8.16 小记
    序幕早上昏迷,九点才到校,少听了四道题,问题不大。点咖啡喝。SAM题也抽象。线段树合并,不会。写个AC自动机板子。\(\color{royalblue}{P3808\【模板】AC\自动机(简单版)}\)板子。\(\text{Link}\)\(\color{royalblue}{P3796\【模板】AC\自动机(加强版)}\)板子。\(\text{Li......
  • D. Trees and Segments
    D.TreesandSegmentsTheteachersoftheSummerInformaticsSchooldecidedtoplant$n$treesinarow,anditwasdecidedtoplantonlyoaksandfirs.Todothis,theymadeaplan,whichcanberepresentedasabinarystring$s$oflength$n$.If$s_i=......
  • 2023.3 Idea配置Tomcat环境
    tomcat配置下载tomcat先到官网(......
  • 2023.8.16 周三:Java论文提交管理系统
    1packageSystem;2importjava.util.Scanner;3publicclassPaperManagement{4publicstaticvoidmain(String[]args){5Scannerscanner=newScanner(System.in);6ScoreInformation[]students=newScoreInformation[5];......
  • 2023.8.16 关于先前函数内外声明变量差异问题的答案
    答案:编译器无法在编译时求得一个非常量的值,它只能在运行时通过读取变量地址来间接得到变量的值,而全局变量在编译时就必须确定其值,故C有静态存储区数据必须用常量初始化的规定。在编译时只能用常量去初始化一个静态存储区的数据,而不能用“读取某个变量的内容”来初始化。来源:外部......
  • CF1844G Tree Weights
    题面传送门这个真的很容易想到吗?首先定\(1\)为根,设每个点的深度是\(d_i\),则两个点之间的距离是\(d_{i}+d_{i+1}-2d_{LCA(i,i+1)}\)。题目中相当于给出了\(n-1\)个方程和\(n-1\)个限制,要解出一个\(n-1\)元的方程。因为限制是从\(1\ton-1\)给出的,所以不可能包含,因此......
  • vue-treeselect 校验失败添加红框
    需求vue-treeselect校验及清除校验这篇介绍了用@input在校验失败时显示校验信息。但还需要同时显示红色边框。如下图所示:解决办法思路:动态绑定类名,校验失败时切换类名,更改边框颜色。子组件SelectTree二次封装vue-treeselect:组件SelectTree<template><divclass=......