bzoj 2733 Splay 启发式合并,名次树

题意:给定一个带点权的无向图,有两种操作:

  1、将两个连通分量合并。

  2、查询某个连通分量里的第K大点。

题解:

用并查集维护连通关系,一开始建立n棵splay树,然后不断合并,查询。

处理技巧:

  1、每个顶点u所在的Splay就是T[find(u)]。

  2、每个顶点在树中对应的节点编号就是该顶点的编号。

  1 #include <cstdio>
  2 #include <iostream>
  3 #define maxn 100110
  4 using namespace std;
  5
  6
  7 int key[maxn], pre[maxn], son[maxn][2], siz[maxn], ntot;
  8 struct Splay {
  9     int root;
 10     Splay():root(0){}
 11     void update( int nd ) {
 12         siz[nd] = siz[son[nd][0]] + siz[son[nd][1]] + 1;
 13     }
 14     void rotate( int nd, int d ) {
 15         int p = pre[nd];
 16         int s = son[nd][!d];
 17         int ss = son[s][d];
 18
 19         son[nd][!d] = ss;
 20         son[s][d] = nd;
 21         if( p ) son[p][ nd==son[p][1] ] = s;
 22         else root = s;
 23
 24         pre[nd] = s;
 25         pre[s] = p;
 26         if( ss ) pre[ss] = nd;
 27
 28         update( nd );
 29         update( s );
 30     }
 31     void splay( int nd, int top=0 ) {
 32         while( pre[nd]!=top ) {
 33             int p = pre[nd];
 34             int nl = nd==son[p][0];
 35             if( pre[p]==top ) {
 36                 rotate( p, nl );
 37             } else {
 38                 int pp = pre[p];
 39                 int pl = p==son[pp][0];
 40                 if( nl==pl ) {
 41                     rotate( pp, pl );
 42                     rotate( p, nl );
 43                 } else {
 44                     rotate( p, nl );
 45                     rotate( pp, pl );
 46                 }
 47             }
 48         }
 49     }
 50     int initnode( int nd, int k, int p ) {
 51         key[nd] = k;
 52         pre[nd] = p;
 53         son[nd][0] = son[nd][1] = 0;
 54         siz[nd] = 1;
 55         return nd;
 56     }
 57     void insert( int k, int nnd ) {
 58         if( !root ) {
 59             root = initnode(nnd,k,0);
 60             return;
 61         }
 62         int nd = root;
 63         while( son[nd][ k>key[nd] ] )
 64             nd = son[nd][ k>key[nd] ];
 65         son[nd][ k>key[nd] ] = initnode(nnd,k,nd);
 66         update( nd );
 67         splay( nd );
 68     }
 69     int nth( int n ) {
 70         int nd = root;
 71         while( 1 ) {
 72             int ls = siz[son[nd][0]];
 73             if( n<=ls ) {
 74                 nd = son[nd][0];
 75             } else if( n>=ls+2 ) {
 76                 nd = son[nd][1];
 77                 n -= ls+1;
 78             } else {
 79                 break;
 80             }
 81         }
 82         splay(nd);
 83         return nd;
 84     }
 85     inline int size() { return siz[root]; }
 86     static void join( Splay &T , int snd ) {
 87         if( !snd ) return;
 88         join( T, son[snd][0] );
 89         join( T, son[snd][1] );
 90         T.insert( key[snd], snd );
 91     }
 92 };
 93
 94 int n, m, q;
 95 int fa[maxn];
 96 Splay T[maxn];
 97
 98 int find( int a ) {
 99     return a==fa[a] ? a : fa[a]=find(fa[a]);
100 }
101
102 void join( int a, int b ) {
103     if( find(a)==find(b) ) return;
104     if( T[find(a)].size() > T[find(b)].size() ) swap(a,b);
105     Splay::join( T[find(b)], T[find(a)].root );
106     fa[find(a)] = find(b);
107 }
108
109 int main() {
110     scanf( "%d%d", &n, &m );
111     for( int i=1,w; i<=n; i++ ) {
112         scanf( "%d", &w );
113         fa[i] = i;
114         T[i].insert( w, i );
115     }
116     for( int i=1,u,v; i<=m; i++ ) {
117         scanf( "%d%d", &u, &v );
118         join(u,v);
119     }
120     scanf( "%d", &q );
121     while( q-- ) {
122         char ch[2];
123         int a, b;
124         scanf( "%s%d%d", ch, &a, &b  );
125         if( ch[0]==‘B‘ ) {
126             join(a,b);
127         } else {
128             if( !(1<=b&&b<=T[find(a)].size()) ) printf( "-1\n" );
129             else printf( "%d\n", T[find(a)].nth(b) );
130         }
131     }
132 }

时间: 2024-10-25 23:33:27

bzoj 2733 Splay 启发式合并,名次树的相关文章

bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)

这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例,submit,1A! 哇真的舒服 调试输出懒得删了QwQ #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<queue> #include

BZOJ 2733 &amp; splay的合并

题意: 带权联通块,添边与查询联通块中第k大. SOL: splay合并+并查集. 我以为splay可以用奇技淫巧来简单合并...调了一下午终于幡然醒悟...于是就只好一个一个慢慢插...什么启发式合并...说的那么高级其实那么丝帛. 实现得非常渣...中序遍历3060ms,换成队列并没有快起来...难道是我的splay本身就不对?... %%%hzw的线段树...发现其实线段树合并更简单更快...被splay蒙蔽了眼睛...思路不够开阔...唉... Code: /*=============

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

BZOJ2733 永无乡【splay启发式合并】

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含

【BZOJ-2809】dispatching派遣 Splay + 启发式合并

2809: [Apio2012]dispatching Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2334  Solved: 1192[Submit][Status][Discuss] Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是

Bzoj 2733: [HNOI2012]永无乡 数组Splay+启发式合并

2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3955  Solved: 2112[Submit][Status][Discuss] Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达

启发式合并&amp;线段树合并&amp;treap合并&amp;splay合并

启发式合并 有\(n\)个集合,每次让你合并两个集合,或询问一个集合中是否存在某个元素. ? 我们可以用平衡树/set维护集合. ? 对于合并两个\(A,B\),如果\(|A|<|B|\),那么我们就把\(A\)中的每个元素暴力加到\(B\)中,否则就把\(B\)中的元素暴力加到\(A\)中. ? 对于一次把\(A\)中的每个元素暴力加到\(B\)中的操作,\(|A|\)会变成\(|A|+|B|\),也就是说大小至少会翻倍,所以一个元素最多被暴力插入\(\log n\)次.每次插入的时间复杂度是

【BZOJ2809】【splay启发式合并】dispatching

Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送.现在你要招募一批忍者,并把它们派遣给顾客.你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算.另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者

HDU - 4358 Boring counting (树上启发式合并/线段树合并)

题目链接 题意:统计树上每个结点中恰好出现了k次的颜色数. dsu on tree/线段树合并裸题. 启发式合并1:(748ms) 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e5+10; 5 int n,m,k,a[N],b[N],nb,fa[N],son[N],siz[N],cnt[N],ans[N],now,ne,hd[N],ka; 6 struct E {