P3224[HNOI2012]永无乡

P3224[HNOI2012]永无乡

(超详细!) 居然没有人写平板电视库的题解(pbds yyds)

不了解 pbds 库的可以去看 oiwiki 或者上网学习。

题目大意

给定一个无向图,询问 \(x\) 所在连通块排名第 \(y\) 的点,且带加边修改。

刚开始每个点属于一个连通块,\(m\) 条边可以看做 \(m\) 个加边的操作。

思路

很容易想到平衡树查排名,对每个连通块维护一棵平衡树,点的排名满足二叉查找树性质。加边分两种情况。加边 \((u,v)\),如果 \(u,v\) 已经属于一个连通块,那么这条边对答案没有影响,不用管他。否则它们不属于一个连通块,加入这条新边相当于合并两个连通块,我们合并两个连通块对应的平衡树即可。

但是由于这两棵平衡树的值域有交,直接合并需要一个一个插入点,复杂度是 \(O(size\log size)\) 的。考虑经典的启发式合并,把的平衡树拆了合并到的平衡树里。

时间复杂度分析(感性理解

我们花费 \(O(size\cdot \log)\) 的复杂度至少能造出一棵大小为 \(2\times size\) 的新树。因此想要卡满时间,我们总是合并大小一样的两棵棵树,因此我们总共要拆的点有 \(o(n\log n)\) 个,合并的复杂度就是 \(O(n\log^2 n)\) 了。

find_by_order(k) insert(x) 函数都是 \(O(n\log n)\) 的。

还有个函数 size() 不清楚时间复杂度(望 dalao 告知),但是为卡常可以自己记录平衡树大小。

code(附带注释)

talk is cheap,show you my code!

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>//pbds 平衡树头文件
// #define LOCAL
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
using namespace std;
using namespace __gnu_pbds;
const int N=1e5+7,Q=3e5+7;
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> tr[N];//主要学会比较函数,其他背下来好了
int n,m,q;
char op;
int u,v;
int w[N],wx[N];// w 是排名,wx 是排名对应的点编号
int id[N];// 点对应的平衡树编号
int siz[N];// 每棵平衡树的大小
void read(){
    op=getchar();
    while(op!='Q'&&op!='B') op=getchar();
}
void merge(int u,int v){// 启发式合并
    if(u==v) return ;
    if(siz[u]<siz[v]) swap(u,v);
    auto it=tr[v].find_by_order(0);// 找到树 v 里排名第 0 即最小的数
    siz[u]+=siz[v];
    // pf("merge %d %d\n",u,v);
    rep(i,1,siz[v]){
        int x=*it;
        // pf("%d\n",x);
        id[wx[x]]=u;
        // tr[v].erase(it);// 卡个常,反正后面也不会用到树 v 所以不删也行
        tr[u].insert(x); //插入数 x
        ++it;
    }
}
int main(){
    #ifdef LOCAL
    freopen("in.txt","r",stdin);
    freopen("my.out","w",stdout);
    #endif
    sf("%d%d",&n,&m);
    rep(i,1,n) {
        sf("%d",&w[i]);
        id[i]=i;
        wx[w[i]]=i;
        tr[i].insert(w[i]);
        siz[i]=1;
    }
    rep(i,1,m){
        sf("%d%d",&u,&v);
        merge(id[u],id[v]);
    }
    sf("%d",&q);
    rep(i,1,q){
        read();
        sf("%d%d",&u,&v);
        if(op=='Q'){
            // pf("Q %d %d\n",u,v);
            u=id[u];
            // pf("%d\n",u);
            if(v>siz[u]) {
                pf("-1\n");
                continue;
            }
            v=*tr[u].find_by_order(v-1);
            // pf("%d\n",v);
            v=wx[v];
            // pf("ans\n");
            pf("%d\n",v);
        }else{
            merge(id[u],id[v]);
        }
    }
}
posted @ 2024-08-13 21:02  liyixin  阅读(11)  评论(0编辑  收藏  举报