• [SDOI2013]森林 主席树+启发式合并


    这题的想法真的很妙啊。
    看到题的第一眼,我先想到树链剖分,并把(DFS)序当成一段区间上主席树。但是会发现在询问的时候,可能会非常复杂,因为你需要把路径拆成很多条轻链和重链,它们还不一定连续,很难做(这个做法貌似可以用于子树第(k)大问题)。
    于是我们换一个思路,让某个点的从它的父亲继承信息,也就是让这个结点对应的主席树维护一条从它到根的链。
    那查询该如何是好?假设要查询的结点为(x,y),我们利用树上差分的思想让(x,y,lca(x,y),fa[lca(x,y)])对应的四棵主席树一起向下跳就行了。
    有连边操作时,就要用到启发式合并了,也就是把小的那棵树合并到大的中,暴力重构一下小树的倍增数组和主席树就行了。
    代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define N 100000
    #define pb push_back
    #define mp make_pair
    #define mid ((l+r)>>1)
    #define pii pair<int, int>
    #define D 17
    
    int n0, n, m, q, T, root[N+5], sz[N+5], d[N+5], me[N+5];
    int w0[N+5], w[N+5], as[N+5], f[N+5][20];
    int nid, sumv[100*N+5], lson[100*N+5], rson[100*N+5];
    int lastans;
    vector<int> G[N+5];
    
    void build(int &o, int l, int r)
    {
        o = ++nid;
        if(l == r) return ;
        build(lson[o], l, mid), build(rson[o], mid+1, r);
    }
    
    int LCA(int x, int y)
    {
        if(d[x] < d[y]) swap(x, y);
        for(int i = D; i >= 0; --i)
            if(d[f[x][i]] >= d[y]) x = f[x][i];
        if(x == y) return x;
        for(int i = D; i >= 0 && f[x][0] != f[y][0]; --i)
            if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    
    void insert(int o, int &u, int l, int r, int x, int k)
    {
        u = ++nid;
        lson[u] = lson[o], rson[u] = rson[o], sumv[u] = sumv[o]+k;
        if(l == r) return ;
        if(x <= mid) insert(lson[o], lson[u], l, mid, x, k);
        else insert(rson[o], rson[u], mid+1, r, x, k);
    }
    
    int query(int x, int y, int k) //x-->y 第k大
    {
        int lca = LCA(x, y), fa = f[lca][0], l = 1, r = n0;
        x = root[x], y = root[y], lca = root[lca], fa = root[fa];
        while(l != r)
        {
            int t = sumv[lson[x]]+sumv[lson[y]]-sumv[lson[fa]]-sumv[lson[lca]];
            if(t >= k) r = mid, x = lson[x], y = lson[y], fa = lson[fa], lca = lson[lca];
            else k -= t, l = mid+1, x = rson[x], y = rson[y], fa = rson[fa], lca = rson[lca];
        }
        return as[l];
    }
    
    void dfs(int u, int fa, int anc)
    {
        d[u] = d[fa]+1, sz[u] = 1, f[u][0] = fa, me[u] = anc;
        for(int i = 1; i <= D; ++i) f[u][i] = f[f[u][i-1]][i-1];
        insert(root[fa], root[u], 1, n0, w[u], 1);
        for(int i = 0, v; i < G[u].size(); ++i)
        {
            v = G[u][i];
            if(v == fa) continue;
            dfs(v, u, anc);
            sz[u] += sz[v];
        }
    }
    
    void link(int x, int y)
    {
        G[x].pb(y), G[y].pb(x);
        if(sz[me[x]] < sz[me[y]]) dfs(x, y, me[y]);
        else dfs(y, x, me[x]);
    }
    
    int main()
    {
        #ifndef ONLINE_JUDGE
            freopen("testdata.in", "r", stdin);
            freopen("testdata.out", "w", stdout);
        #endif
        scanf("%d", &T);
        scanf("%d%d%d", &n, &m, &q);
        for(int i = 1; i <= n; ++i) scanf("%d", &w0[i]), w[i] = w0[i]; //离散化
        sort(w0+1, w0+n+1);
        n0 = unique(w0+1, w0+n+1)-w0-1;
        for(int i = 1, t; i <= n; ++i)
        {
            t = lower_bound(w0+1, w0+n0+1, w[i])-w0;
            as[t] = w[i], w[i] = t;
        }
        for(int i = 1, x, y; i <= m; ++i)
        {
            scanf("%d%d", &x, &y);
            G[x].pb(y), G[y].pb(x);
        }
        build(root[0], 1, n0);
        for(int i = 1; i <= n; ++i)
            if(!d[i]) dfs(i, 0, i);
        char c;
        for(int i = 1, x, y, z; i <= q; ++i)
        {
            cin >> c;
            scanf("%d%d", &x, &y);
            x ^= lastans, y ^= lastans;
            if(c == 'Q') scanf("%d", &z), z ^= lastans, printf("%d
    ", lastans = query(x, y, z));
            else link(x, y);
        }
        return 0;
    }
    
  • 相关阅读:
    01、MySQL_简介
    算法—打擂台法
    第10章 对文件的输入输出
    第9章 用户自己建立数据类型
    Spring Cloud
    JUC
    Swagger Learing
    JUC
    Spring Data
    SpringCloud
  • 原文地址:https://www.cnblogs.com/dummyummy/p/10106401.html
Copyright © 2020-2023  润新知