LP3302 [SDOI2013]森林 [主席树/启发式合并]

gayyyy

Problem

小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有M条边。

小Z希望执行T个操作,操作有两类:

  • Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。
  • L x y在点x和点y之间连接一条边。保证完成此操作后,仍然是一片森林。

强制在线

Solution

启发式合并

倍增处理lca

卡卡空间

然后就行辣

复习下板子

Code

// Code by ajcxsu
// Problem: forest

#include<bits/stdc++.h>
#define CLR(x) memset(x, 0, sizeof(x))
using namespace std;

template<typename T> inline void gn(T &x) {
    char ch=getchar(), pl=0;
    x=0;
    while(ch<'0' || ch>'9') pl=(ch=='-'), ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0', ch=getchar();
    x*=pl?-1:1;
}
inline void gc(char &c) {
    c=getchar();
    while(!isalpha(c)) c=getchar();
}

const int N=8e4+10, M=2.1105e7, MM=2e5+10, OP=18;
int h[N], to[MM], nexp[MM], p=1;
inline void ins(int a, int b) { nexp[p]=h[a], h[a]=p, to[p]=b, p++; }
int siz[N], dep[N];

/* chairman tree */
struct Node *nil;
struct Node {
    Node *ls, *rs;
    int v;
    Node(int val=0):v(val) { ls=rs=nil; }
} *nd[N], po[M], *pp=po;

void updata(Node *&x, int l, int r, int d) {
    Node *nd=pp++;
    *nd=*x, x=nd, x->v++;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(d<=mid) updata(x->ls, l, mid, d);
    else updata(x->rs, mid+1, r, d);
}

int query(Node *a, Node *b, Node *c, Node *d, int l, int r, int k) {
    if(l==r) return l;
    int v=a->ls->v+b->ls->v-c->ls->v-d->ls->v, mid=(l+r)>>1;
    if(k<=v) return query(a->ls, b->ls, c->ls, d->ls, l, mid, k);
    else return query(a->rs, b->rs, c->rs, d->rs, mid+1, r, k-v);
}

/* lca */
int gup[N][OP];
int lca(int s, int t) {
    if(dep[s]<dep[t]) swap(s, t);
    for(int j=OP-1;j>=0;j--)
        if(dep[gup[s][j]]>=dep[t]) s=gup[s][j];
    if(s!=t) {
        for(int j=OP-1;j>=0;j--)
            if(gup[s][j]!=gup[t][j]) s=gup[s][j], t=gup[t][j];
        s=gup[s][0];
    }
    return s;
}

/* Union */
int n, m, t, k;
int lis[N], arr[N];
void dfs(int x, int fa) {
    dep[x]=dep[fa]+1, gup[x][0]=fa, nd[x]=nd[fa], updata(nd[x], 1, k, arr[x]);
    for(int j=1;j<OP;j++) gup[x][j]=gup[gup[x][j-1]][j-1];
    for(int u=h[x];u;u=nexp[u])
        if(to[u]!=fa) dfs(to[u], x);
}

/* UF */
int fa[N];
int Find(int x) { return fa[x]?fa[x]=Find(fa[x]):x; }
bool Union(int a, int b) {
    int af=Find(a), bf=Find(b);
    if(af==bf) return false;
    if(siz[af]>siz[bf]) swap(af, bf), swap(a, b);
    dfs(a, b);
    fa[af]=bf, siz[bf]+=siz[af];
    return true;
}

map<int, int> mp;
/* init */
void ini() {
    pp=po, nil=pp++, *nil=Node(), nil->ls=nil->rs=nil;
    fill(nd, nd+N, nil);
    fill(siz+1, siz+1+n, 1), fill(dep+1, dep+1+n, 1);
}

int main() {
    ios::sync_with_stdio(false);
    gn(n), gn(n), gn(m), gn(t);
    ini();
    int lst=0;
    for(int i=1;i<=n;i++) gn(arr[i]), lis[i]=arr[i];
    sort(lis+1, lis+1+n), k=unique(lis+1, lis+1+n)-lis-1;
    for(int i=1;i<=k;i++) mp[lis[i]]=i;
    for(int i=1;i<=n;i++) arr[i]=mp[arr[i]], updata(nd[i], 1, k, arr[i]);
    int u, v;
    for(int i=1;i<=m;i++) gn(u), gn(v), ins(u, v), ins(v, u), Union(u, v);
    char con;
    int a, b, c;
    while(t--) {
        gc(con), gn(a), gn(b), a^=lst, b^=lst;
        if(con=='Q') {
            gn(c), c^=lst;
            int l=lca(a, b), lf=gup[l][0];
            printf("%d\n", lst=lis[query(nd[a], nd[b], nd[l], nd[lf], 1, k, c)]);
        }
        else if(con=='L') ins(a, b), ins(b, a), Union(a, b);
    }
    return 0;
}

本文链接:https://acxblog.site/archives/sol-luogu-3302.html
本文采用 CC BY-NC-SA 3.0 协议进行许可