Luogu 3302 [SDOI2013]森林

BZOJ 3123

丑陋的常数,BZOJ 19968ms 卡过。

感觉几个思想都挺经典但是挺套路的。

先考虑不连边的情况怎么做,区间第$k$小,想到主席树,每一个结点维护它到根的前缀和,这样子每一次查询$x$到$y$链上的点就相当于主席树上$sum(x) + sum(y) - sum(lca(x, y)) - sum(fa(lca(x, y)))$,时间复杂度一个$log$。

然后想一想连边怎么做,因为我们要维护一个森林的形态,如果暴力连边的话,相当于把$y$作为根建树连到$x$上,我们有一个优化暴力的办法就是每一次都把$siz$小的那一个点建树连到另一个点。

然后发现这样的复杂度就已经对了,证明方法和树剖类似,相当于每一次合并$siz$都会至少扩大一倍,所以对于一个大小为$n$的集合,至多会合并$logn$次,总复杂度$O(nlogn)$,这东西有一个名字叫做启发式合并。

时间复杂度$O(nlog^{2}n)$。

还有一个算是坑点的东西:

(我也是)

虽然感觉题目说得很清楚了,但是我还是没有看

Code:

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

const int N = 80005;
const int Lg = 18;
const int M = 4e7 + 5;
const int inf = 1 << 30;

int testCase, n, m, qn, tot, head[N];
int ans, maxn, ufs[N], siz[N];
int fa[N][Lg], dep[N], a[N], v[N];
bool vis[N];

struct Edge {
    int to, nxt;
} e[N << 1];

inline void add(int from, int to) {
    e[++tot].to = to;
    e[tot].nxt = head[from];
    head[from] = tot;
}

struct Innum {
    int val, id;
} in[N];

bool cmp(const Innum &x, const Innum &y) {
    if(x.val != y.val) return x.val < y.val;
    else return x.id < y.id;
}

inline void chkMax(int &x, int y) {
    if(y > x) x = y;
}

inline void discrete() {
    sort(in + 1, in + 1 + n, cmp);
    in[0].val = -inf;
    
    for(int cnt = 0, i = 1; i <= n; i++) {
        if(in[i].val != in[i - 1].val) chkMax(maxn, ++cnt);
        a[in[i].id] = cnt;
        v[cnt] = in[i].val;
    }
}

inline void read(int &X) {
    X = 0; char ch = 0; int op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

inline void init() {
    for(int i = 1; i <= n; i++)
        ufs[i] = i, siz[i] = 1;
}

int find(int x) {
    return ufs[x] == x ? x : ufs[x] = find(ufs[x]);
}

namespace PSegT {
    struct Node {
        int lc, rc, sum;
    } s[M];
    int root[N], nodeCnt;
    
    #define mid ((l + r) >> 1)
    
    inline void up(int p) {
        if(!p) return;
        s[p].sum = s[s[p].lc].sum + s[s[p].rc].sum;
    }
    
    void ins(int &p, int l, int r, int x, int pre) {
        s[p = ++nodeCnt] = s[pre], s[p].sum++;
        if(l == r) return;
        
        if(x <= mid) ins(s[p].lc, l, mid, x, s[pre].lc);
        else ins(s[p].rc, mid + 1, r, x, s[pre].rc);
        
        up(p);
    }
    
    int query(int x, int y, int z, int w, int l, int r, int k) {
        if(l == r) return l;
        
        int now = s[s[x].lc].sum + s[s[y].lc].sum - s[s[z].lc].sum - s[s[w].lc].sum;
        
        if(k <= now) return query(s[x].lc, s[y].lc, s[z].lc, s[w].lc, l, mid, k);
        else return query(s[x].rc, s[y].rc, s[z].rc, s[w].rc, mid + 1, r, k - now);
    }
    
} using namespace PSegT;

void dfs1(int x, int fat) {
    vis[x] = 1, dep[x] = dep[fat] + 1, fa[x][0] = fat;
    for(int i = 1; i <= 16; i++)
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for(int i = head[x]; i; i = e[i].nxt) {
        int y = e[i].to;
        if(y == fat) continue;
        dfs1(y, x);
    }
}

inline void swap(int &x, int &y) {
    int t = x; x = y; y = t;
}

inline int getLca(int x, int y) {
    if(dep[x] < dep[y]) swap(x, y);
    for(int i = 16; i >= 0; i--)
        if(dep[fa[x][i]] >= dep[y])
            x = fa[x][i];
    if(x == y) return x;
    for(int i = 16; i >= 0; i--)    
        if(fa[x][i] != fa[y][i])    
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}

void dfs2(int x) {
    root[x] = 0, ins(root[x], 1, maxn, a[x], root[fa[x][0]]);
    for(int i = head[x]; i; i = e[i].nxt) {
        int y = e[i].to;
        if(y == fa[x][0]) continue;
        dfs2(y);
    }
}

inline void solve() {
    int x, y, k;
    read(x), read(y), read(k);
    x ^= ans, y ^= ans, k ^= ans;
    int z = getLca(x, y), w = fa[z][0];
    int res = query(root[x], root[y], root[z], root[w], 1, maxn, k);
    printf("%d\n", ans = v[res]);
}

int main() {
    read(testCase);
    for(testCase = 1; testCase--; ) {
        read(n), read(m), read(qn);
        
        memset(head, 0, sizeof(head)); tot = maxn = 0;
        init();
        
        for(int i = 1; i <= n; i++) {
            read(a[i]);
            in[i] = (Innum) {a[i], i};
        }
        discrete();
        
        for(int x, y, i = 1; i <= m; i++) {
            read(x), read(y);
            add(x, y), add(y, x);
            
            int fx = find(x), fy = find(y);
            if(fx != fy) {
                ufs[fx] = fy;
                siz[fy] += siz[fx];
            }
        }
        
        memset(vis, 0, sizeof(vis));
        memset(root, 0, sizeof(root)); nodeCnt = 0;
        for(int i = 1; i <= n; i++) {
            if(vis[i]) continue;
            dfs1(find(i), 0), dfs2(find(i));
        }
        
        ans = 0;
        for(char op[3]; qn--; ) {
            scanf("%s", op);
            if(op[0] == 'Q') solve();
            else {
                int x, y; 
                read(x), read(y);
                x ^= ans, y ^= ans;
                
                int fx = find(x), fy = find(y);
                if(siz[fx] > siz[fy]) swap(fx, fy);
                
                add(x, y), add(y, x);
                dfs1(y, x), dfs2(y);
                
                ufs[fy] = fx, siz[fx] += siz[fy];
            }
        }
        
    }
    return 0;
}
View Code

 

posted @ 2018-09-04 18:54  CzxingcHen  阅读(113)  评论(0编辑  收藏  举报