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]);
}
}
}
标签:连通,int,永无,HNOI2012,P3224,平衡,size,复杂度,op
From: https://www.cnblogs.com/liyixin0514/p/18357683