码迷,mamicode.com
首页 > 其他好文 > 详细

【SDOI 2013】森林

时间:2020-05-31 13:13:42      阅读:52      评论:0      收藏:0      [点我收藏+]

标签:while   时间复杂度   sdoi   space   main   pac   自己   证明   using   

博主的 BiBi 时间

简直不敢相信自己去年 \(9\) 月学过主席树。。。真的一点印象都没了(真的太菜了)。

另外,这题有个坑点:\(testcase\) 是数据编号,不是数据组数!

Solution

主席树一般是处理区间第几大/小的问题。然而这次的场景转移到了树上。

我们可以让 \(rt[u]\)\(rt[fa[u]]\) 的基础上建树,这样就可以用 \(sum[u]+sum[v]-sum[lca(u,v)]-sum[fa[lca(u,v)]]\) 来表示这个路径的某种数值的个数。这里的数值离散化处理就行了。

加边操作我们就用启发式合并。每次选择树的大小较小的重建。

证明一下启发式合并的复杂度。我们对于每个点考虑,考虑一直合并上去的情况(启发式合并就是更改大小较小的那一坨,所以它的复杂度是取决于小坨的大小而不是大坨的),因为是合并上去(这是小坨),合并之后整个坨的大小大于等于小坨的大小 \(*2\)。所以这个点总共最多合并 \(logn\) 次,又因为是每个点考虑的,其实这个点的合并次数就是这个点的复杂度,总复杂度我们累加即可:\(O(nlogn)\)

我们算一下空间复杂度。首先我们初始建树是每个点都会建一棵树,每棵树最多加 \(logn\) 个点,所以这个空间复杂度为 \(S(nlogn)\)

关于后面重建的空间复杂度。之前的是 \(nlogn\) 的重建次数(证明在启发式合并证明),每次重建最多加 \(logn\) 个点,所以空间复杂度 \(S(nlogn^2)\)

总时间复杂度应该是 \(O(nlogn^2)\)

博主蒟蒻,如有错误请在评论区指出,我会在第一时间更改。

Code

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

const int N = 8e4 + 5;

int n, m, q, a[N], b[N], len, cnt, head[N], nxt[N << 2], to[N << 2], sz, lc[N * 276], rc[N * 276], sum[N * 276], rt[N], siz[N], dep[N], f[N][18], p[5], lastans, root[N];
bool vis[N];

int read() {
    int x = 0, f = 1; char s;
    while((s = getchar()) < ‘0‘ || s > ‘9‘) if(s == ‘-‘) f = -1;
    while(s >= ‘0‘ && s <= ‘9‘) {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
    return x * f;
}

void addEdge(const int u, const int v) {
    nxt[++ cnt] = head[u], to[cnt] = v, head[u] = cnt;
}

void update(int &o, const int fa, const int l, const int r, const int p) {
    if(l > p || r < p) return;
    o = ++ sz; lc[o] = lc[fa], rc[o] = rc[fa], sum[o] = sum[fa] + 1;
    if(l == r) return;
    int mid = l + r >> 1;
    update(lc[o], lc[fa], l, mid, p); update(rc[o], rc[fa], mid + 1, r, p);
}

void dfs(const int u, const int fa, const int Root) {
    root[u] = Root; vis[u] = siz[u] = 1; f[u][0] = fa; dep[u] = dep[fa] + 1;
    update(rt[u], rt[fa], 1, len, a[u]);
    for(int i = 1; (1 << i) <= dep[u]; ++ i) f[u][i] = f[f[u][i - 1]][i - 1];
    for(int i = head[u]; i; i = nxt[i]) {
        int v = to[i];
        if(v == fa) continue;
        dfs(v, u, Root);
        siz[u] += siz[v];
    }
}

int lca(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    int d = dep[v] - dep[u];
    for(int i = 0; (1 << i) <= d; ++ i) if((1 << i) & d) v = f[v][i];
    if(u == v) return u;
    for(int i = 16; ~i; -- i)
        if(f[u][i] != f[v][i] && (1 << i) <= dep[v]) u = f[u][i], v = f[v][i];
    return f[u][0];
}

int query(const int l, const int r, const int k) {
    if(l == r) return l;
    int x = sum[lc[p[1]]] + sum[lc[p[2]]] - sum[lc[p[3]]] - sum[lc[p[4]]];
    int mid = l + r >> 1;
    if(x >= k) {
        for(int i = 1; i <= 4; ++ i) p[i] = lc[p[i]];
        return query(l, mid, k);
    }
    else {
        for(int i = 1; i <= 4; ++ i) p[i] = rc[p[i]];
        return query(mid + 1, r, k - x);
    }
}

void init() {
    sz = cnt = lastans = 0;
    memset(vis, 0, sizeof vis);
    memset(head, 0, sizeof head);
}

int main() {
    int u, v, k, T = read(); char s[10];
    T = 1;
    for(; T; -- T) {
        n = read(), m = read(), q = read(); init();
        for(int i = 1; i <= n; ++ i) a[i] = b[i] = read();
        sort(b + 1, b + n + 1);
        len = unique(b + 1, b + n + 1) - b - 1;
        for(int i = 1; i <= n; ++ i) a[i] = lower_bound(b + 1, b + len + 1, a[i]) - b;
        for(int i = 1; i <= m; ++ i) {
            u = read(), v = read();
            addEdge(u, v), addEdge(v, u);
        }
        for(int i = 1; i <= n; ++ i) if(! vis[i]) dfs(i, 0, i);
        while(q --) {
            scanf("%s", s);
            if(s[0] == ‘Q‘) {
                u = read() ^ lastans, v = read() ^ lastans, k = read() ^ lastans;
                int LCA = lca(u, v);
                p[1] = rt[u], p[2] = rt[v], p[3] = rt[LCA], p[4] = rt[f[LCA][0]];
                printf("%d\n", lastans = b[query(1, len, k)]);
            }
            else {
                u = read() ^ lastans, v = read() ^ lastans;
                if(siz[root[u]] < siz[root[v]]) swap(u, v);
                siz[root[u]] += siz[root[v]];
                addEdge(u, v), addEdge(v, u);
                dfs(v, u, root[u]);
            }
        }
    }
    return 0;
}
/*
1
20 12 20
412060525 42425138 67114752 160822495 201962681 926214957 380263349 733667141 869039239 641017702 154667400 461702107 438851950 176272938 209229857 985208975 762952138 936593832 409183276 999506034
2 17
4 1
15 3
3 10
9 10
7 16
19 15
13 2
6 2
3 14
7 18
8 15
Q 6 17 2
Q 762952152 762952154 762952139
L 380263329 380263352
L 380263357 380263333
Q 380263356 380263359 380263348
L 641017709 641017703
L 641017716 641017700
Q 641017716 641017704 641017698
Q 380263359 380263357 380263345
L 733667140 733667136
L 733667150 733667156
L 733667145 733667142
Q 733667149 733667145 733667140
Q 67114772 67114761 67114759
Q 733667145 733667159 733667142
Q 380263359 380263356 380263351
Q 869039233 869039232 869039237
Q 380263359 380263345 380263356
Q 733667136 733667140 733667143
Q 412060537 412060516 412060519
*/

【SDOI 2013】森林

标签:while   时间复杂度   sdoi   space   main   pac   自己   证明   using   

原文地址:https://www.cnblogs.com/AWhiteWall/p/12993605.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!