珂朵莉树及一些相关题目

比分块还要暴力、比线段树还要好写的功能萎缩而强大的数据结构。只要有类似于区间赋值的操作且数据随机,珂朵莉树就可以完成几乎所有查询。

先从一道例题说起:
CF896C
也许是珂朵莉树名字的由来?由于珂朵莉树更加珂学所以人们渐渐放弃了老司机树或者ODT的叫法
区间赋值、数据随机,所以我们就要上乱搞了。很显然,经过若干次随机操作后,假如把数字相同的一段区间看成一个点的话,原序列只会剩下\(logn\)个点。这就启发我们,就是要把区间缩成一个点。
既然是一个点又要代表一段区间,那结构体就再合适不过了。
同时,我们把这些区间整理在一起,用一个\(set\)维护。

struct node {
    int l, r;
    mutable ll val; //由于下面区间加的操作要修改val值,所以要加mutable
    inline node(int _l, int _r = 0, ll _val = 0) : l(_l), r(_r), val(_val) {}
    inline int operator < (const node &a) const {
        return l < a.l;
    }
};
set <node> s;

珂朵莉树基本操作:

\(split\)

inline set<node>::iterator split(int x)
{
    set<node>::iterator it = s.lower_bound(node(x));
    if (it != s.end() && it->l == x) return it;
    it--;
    int l = it->l, r = it->r;
    ll val = it->val;
    s.erase(it);
    s.insert(node(l, x - 1, val));
    return s.insert(node(x, r, val)).first; //返回你插入的位置,insert的一个神仙用法
}

\([l, r]\)的区间分成\([l, x - 1]\)\([x, r]\),这样\(x\)就成了一个区间的左端点,方便后续处理。

\(assign\)

inline void assign(int l, int r, int x)
{
    set<node>::iterator itr = split(r + 1), itl = split(l); //注意这个细节,正着写会RE
    s.erase(itl, itr); //删掉一段左闭右开的区间,erase的一个神仙用法
    s.insert(node(l, r, x));
}

代码复杂度堪比树状数组……把原来的全删了,再加一个回去,复杂度就是在这里变松了。
其它操作就简单粗暴了……

inline void add(int l, int r, ll x)
{
    set<node>::iterator itr = split(r + 1), itl = split(l);
    while (itl != itr) itl->val += x, itl++;
}

inline ll get_kth(int l, int r, int k)
{
    set<node>::iterator itr = split(r + 1), itl = split(l);
    vector<pair<ll, int> > v;
    while (itl != itr) v.push_back(make_pair(itl->val, itl->r - itl->l + 1)), itl++;
    sort(v.begin(), v.end());
    for (vector<pair<ll, int> >::iterator it = v.begin(); it != v.end(); it++) {
        if (k <= it->second) return it->first;
        k -= it->second;
    }
    return 0;
}

inline int ksm(int a, int n, int mod)
{
    int b = 1;
    while (n) {
        if (n & 1) b = 1ll * a * b % mod;
        a = 1ll * a * a % mod;
        n >>= 1;
    }
    return b;
}

inline int sum(int l, int r, int x, int mod)
{
    set<node>::iterator itr = split(r + 1), itl = split(l);
    int res = 0;
    while (itl != itr) res = (res + 1ll * (itl->r - itl->l + 1) * ksm(itl->val % mod, x, mod)) % mod, itl++;
    return res;
}

同样注意\(itr\)要先写就好了。

然后是一些板题(都是线段树题但信奉珂学的话就要用珂朵莉树做)
P2572
一个字,板。
CF915E
注意\(assign\)的同时要处理好答案,如果每次都暴力扫整个\(set\)的话会\(T\)
P2486
珂朵莉上树
不,是珂朵莉树上树。
查询比较麻烦,要注意有序树剖的各种细节问题,开两个\(vector\),一个存\(x\)\(lca\)的,另一个存\(y\)\(lca\)的,然后把第二个\(vector\)倒着接到第一个上面,最后暴力统计答案。
由于在树上依然会经常有很长的链被覆盖成一种颜色,维护一条重链的\(set\)\(size\)可能会非常小,所以我猜想复杂度会比\(nlog^2n\)要低。
贴下代码吧:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>

const int maxn = 1e5 + 7;

using namespace std;

int a[maxn];
int to[maxn << 1];
int nex[maxn << 1];
int last[maxn], k;
int fa[maxn];
int son[maxn];
int sz[maxn];
int dep[maxn];
int top[maxn];
int dfn[maxn], id;
struct node {
    int l, r, val;
    inline node(int _l, int _r = 0, int _val = 0) : l(_l), r(_r), val(_val) {}
    inline int operator < (const node &a) const {
        return l < a.l;
    }
};
set <node> s;
vector <node> v[2];

inline int read()
{
    int X = 0; char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') X = X * 10 + ch - '0', ch = getchar();
    return X;
}

inline void add_edge(int x, int y)
{
    to[++k] = y; nex[k] = last[x]; last[x] = k;
}

inline set<node>::iterator split(int x)
{
    set<node>::iterator it = s.lower_bound(node(x));
    if (it != s.end() && it->l == x) return it;
    it--;
    int l = it->l, r = it->r, val = it->val;
    s.erase(it);
    s.insert(node(l, x - 1, val));
    return s.insert(node(x, r, val)).first;
}

inline void assign(int l, int r, int val)
{
    set<node>::iterator itr = split(r + 1), itl = split(l);
    s.erase(itl, itr);
    s.insert(node(l, r, val));
}

inline void get(int l, int r, int type)
{
    set<node>::iterator itr = split(r + 1), itl = split(l);
    itr--;
    while (1) {
        v[type].push_back(*itr);
        if (itl == itr) break;
        itr--;
    }
}

inline int lca(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;
}

inline void upchain(int x, int y, int z)
{
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        assign(dfn[top[x]], dfn[x], z);
        x = fa[top[x]];
    }
    if (dep[x] > dep[y]) swap(x, y);
    assign(dfn[x], dfn[y], z);
}

inline int sum(int x, int y)
{
    v[0].clear();
    v[1].clear();
    int g = lca(x, y);
    while (top[x] != top[g]) {
        get(dfn[top[x]], dfn[x], 0);
        x = fa[top[x]];
    }
    get(dfn[g], dfn[x], 0);
    while (top[y] != top[g]) {
        get(dfn[top[y]], dfn[y], 1);
        y = fa[top[y]];
    }
    get(dfn[g], dfn[y], 1);
    for (vector<node>::iterator it = v[1].end() - 1;; it--) {
        v[0].push_back(*it);
        if (it == v[1].begin()) break;
    }
    int pre = 0, ans = 0;
    for (vector<node>::iterator it = v[0].begin(); it != v[0].end(); it++)
        if (it->val != pre) pre = it->val, ans++;
    return ans;
}

void dfs1(int x, int f)
{
    sz[x] = 1;
    fa[x] = f;
    dep[x] = dep[f] + 1;
    for (int i = last[x]; i; i = nex[i]) {
        int y = to[i];
        if (y == f) continue;
        dfs1(y, x);
        sz[x] += sz[y];
        if (sz[y] > sz[son[x]]) son[x] = y;
    }
}

void dfs2(int x, int f)
{
    top[x] = f;
    dfn[x] = ++id;
    s.insert(node(id, id, a[x]));
    if (!son[x]) return;
    dfs2(son[x], f);
    for (int i = last[x]; i; i = nex[i])
        if (to[i] != fa[x] && to[i] != son[x]) dfs2(to[i], to[i]);
}

int main(void)
{
    int n = read(), m = read();
    for (int i = 1; i <= n; i++) a[i] = read();
    for (int i = 1; i < n; i++) {
        int x = read(), y = read();
        add_edge(x, y);
        add_edge(y, x);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    while (m--) {
        char opt;
        scanf(" %c", &opt);
        int x = read(), y = read();
        if (opt == 'C') upchain(x, y, read());
        else printf("%d\n", sum(x, y));
    }
    
    return 0;
}
posted @ 2019-03-08 19:55  star_city  阅读(351)  评论(0编辑  收藏  举报