LA 5031 Graph and Queries —— Treap名次树
离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度O(n1logn2),由于合并之后原本结点数少的子树结点数至少翻倍,所以每个结点最多被插入 logn 次,故总时间复杂度为
O(n log2n) 。
注意细节处理,代码如下:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector> using namespace std; struct Node {
Node *ch[];
int r;
int v;
int s;
Node(int vv): v(vv) {
s = ;
ch[] = ch[] = NULL;
r = rand();
}
int cmp(int x) const {
if(x == v) return -;
return x < v ? : ;
}
void maintain() {
s = ;
if(ch[] != NULL) s += ch[]->s;
if(ch[] != NULL) s += ch[]->s;
}
}; void rotate(Node* &o, int d) {
Node* k = o->ch[d^]; o->ch[d^] = k->ch[d]; k->ch[d] = o;
o->maintain(); k->maintain(); o = k;
} void insert(Node* &o, int x) {
if(o == NULL) o = new Node(x);
else {
int d = x < o->v ? : ;
insert(o->ch[d], x);
if(o->ch[d]->r > o->r) rotate(o, d^);
}
o->maintain();
}
void remove(Node* &o, int x) {
int d = o->cmp(x);
Node* u = o;
if(d == -) {
if(o->ch[] != NULL && o->ch[] != NULL){
int d2 = o->ch[]->r > o->ch[]->r ? : ;
rotate(o, d2);
remove(o->ch[d2], x);
}
else {
if(o->ch[] == NULL) o = o->ch[]; else o = o->ch[];
delete u;
}
}
else remove(o->ch[d], x);
if(o != NULL) o->maintain();
} int kth(Node* o, int k) {
if(o == NULL || k > o->s || k <= ) return ;
int s = o->ch[] == NULL ? : o->ch[]->s;
if(k == s+) return o->v;
else if(k <= s) return kth(o->ch[], k);
else return kth(o->ch[], k-s-);
} struct cmd {
char type;
int x, p;
}; vector<cmd> cmds; const int maxn = 2e4 + ;
const int maxm = 6e4 + ;
int n, m;
int weight[maxn], from[maxm], to[maxm], removed[maxm]; int pa[maxn];
int findpa(int x) {return x == pa[x] ? x : pa[x] = findpa(pa[x]);}
long long sum;
int cnt;
Node* root[maxn]; void mergetreeto(Node* &ser, Node* &to) {
if(ser->ch[] != NULL) mergetreeto(ser->ch[], to);
if(ser->ch[] != NULL) mergetreeto(ser->ch[], to);
insert(to, ser->v);
delete ser;
ser = NULL;
} void removetree(Node *&ser) {
if(ser == NULL) return;
if(ser->ch[] != NULL) removetree(ser->ch[]);
if(ser->ch[] != NULL) removetree(ser->ch[]);
delete ser;
ser = NULL;
} void add_edge(int id) {
int x = findpa(pa[from[id]]);
int y = findpa(pa[to[id]]);
if(x != y) {
if(root[x]->s < root[y]->s) mergetreeto(root[x], root[y]), pa[x] = y;
else mergetreeto(root[y], root[x]), pa[y] = x;
}
} void querycnt(int x, int k) {
cnt++;
sum += kth(root[findpa(x)], k);
} void change_w(int x, int v) {
int u = findpa(pa[x]);
remove(root[u], weight[x]);
insert(root[u], v);
weight[x] = v;
} void init() {
cmds.clear();
cnt = ;
sum = ;
memset(removed, , sizeof removed);
for(int i = ; i < n; i++) removetree(root[i]);
}
int main() {
int kase = ;
while(scanf("%d%d", &n, &m) == && n) {
for(int i = ; i <= n; i++) scanf("%d", &weight[i]);
for(int i = ; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
from[i] = u;
to[i] = v;
}
init();
while() {
getchar();
char ch;
scanf("%c", &ch);
cmd C;
C.type = ch;
C.x = C.p = ;
if(ch == 'E') break;
scanf("%d", &C.x);
if(ch == 'D') removed[C.x] = ;
if(ch == 'Q') scanf("%d", &C.p);
if(ch == 'C') {
scanf("%d", &C.p);
swap(C.p, weight[C.x]);
}
cmds.push_back(C);
}
for(int i = ; i <= n; i++) {
pa[i] = i;
root[i] = new Node(weight[i]);
}
for(int i = ; i <= m; i++)
if(!removed[i]) add_edge(i); for(int i = cmds.size()-; i >= ; i--) {
cmd C = cmds[i];
if(C.type == 'D') add_edge(C.x);
if(C.type == 'C') change_w(C.x, C.p);
if(C.type == 'Q') querycnt(C.x, C.p);
}
printf("Case %d: %.6lf\n", ++kase, sum/double(cnt));
}
return ;
}
LA 5031 Graph and Queries —— Treap名次树的更多相关文章
- LA - 5031 - Graph and Queries
题意:一个N个点(编号从1开始),M条边的无向图(编号从1开始),有3种操作: D X:把编号为X的边删了: Q X K:查询编号为X的结点所在连通分量第K大的元素: C X V:将编号为X的结点的权 ...
- UVaLive 5031 Graph and Queries (Treap)
题意:初始时给出一个图,每个点有一个权值,三种操作:(1)删除某个边:(2)修改每个点的权值:(3)询问与节点x在一个连通分量中所有点的第K大的权值. 析:首先是要先离线,然后再倒着做,第一个操作就成 ...
- uvalive 5031 Graph and Queries 名次树+Treap
题意:给你个点m条边的无向图,每个节点都有一个整数权值.你的任务是执行一系列操作.操作分为3种... 思路:本题一点要逆向来做,正向每次如果删边,复杂度太高.逆向到一定顺序的时候添加一条边更容易.详见 ...
- UVa 1479 (Treap 名次树) Graph and Queries
这题写起来真累.. 名次树就是多了一个附加信息记录以该节点为根的树的总结点的个数,由于BST的性质再根据这个附加信息,我们可以很容易找到这棵树中第k大的值是多少. 所以在这道题中用一棵名次树来维护一个 ...
- HDU 3726 Graph and Queries treap树
题目来源:HDU 3726 Graph and Queries 题意:见白书 思路:刚学treap 參考白皮书 #include <cstdio> #include <cstring ...
- 「模板」「讲解」Treap名次树
Treap实现名次树 前言 学平衡树的过程可以说是相当艰难.浏览Blog的过程中看到大量指针版平衡树,不擅长指针操作的我已经接近崩溃.于是,我想着一定要写一篇非指针实现的Treap的Blog. 具体如 ...
- UVALive 5031 Graph and Queries (Treap)
删除边的操作不容易实现,那么就先离线然后逆序来做. 逆序就变成了合并,用并存集判断连通,用Treap树来维护一个连通分量里的名次. Treap = Tree + Heap.用一个随机的优先级来平衡搜索 ...
- UVALive - 5031 Graph and Queries (并查集+平衡树/线段树)
给定一个图,支持三种操作: 1.删除一条边 2.查询与x结点相连的第k大的结点 3.修改x结点的权值 解法:离线倒序操作,平衡树or线段树维护连通块中的所有结点信息,加个合并操作就行了. 感觉线段树要 ...
- UVALive5031 Graph and Queries(Treap)
反向操作,先求出最终状态,再反向操作. 然后就是Treap 的合并,求第K大值. #include<cstdio> #include<iostream> #include< ...
随机推荐
- AC自动机fail树小结
建议大家学过AC自动机之后再来看这篇小结 fail树就是讲fail指针看做一条边连成的树形结构 fail指针在AC自动机中的含义是指以x为结尾的后缀在其他模式串中所能匹配的最长前缀的长度 所以在模式串 ...
- Leetcode693.Binary Number with Alternating Bits交替位二进制数
给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等. 示例 1: 输入: 5 输出: True 解释: 5的二进制数是: 101 示例 2: 输入: 7 输出 ...
- 初探uni-app
第一步:下载 第二步:安装 第三步:创建项目 第四步:项目目录 项目运行 项目效果为 打包为原生App(云端) 运行打包 使用vue 效果如下,并不好看 使用代码如下 未完,待续.... 就是对着官网 ...
- python中字母的大小写转换
1. capitalize(): 首字母大写,其余全部小写 2. upper() :全转换成大写 3. lower(): 全转换成小写 4. title() :标题首字大写,如 &q ...
- docker入门 基础命令 docker安装
docker入门 在学一门新知识的时候,超哥喜欢提问,why?what?how? wiki资料 什么是docker Docker 最初是 dotCloud 公司创始人 Solomon Hykes ...
- sublime配置node开发环境
下载node插件 https://github.com/tanepiper/SublimeText-Nodejs 下载zip压缩包后解压,文件名改为Node 打开文件“Nodejs.sublime-s ...
- swiper踩过的哪些坑
最近,苦恼于各种轮播的需求,每个自己都要自己写,写的挺烦的.终于,在网上发现了swiper插件,发现还是挺实用的,但其中还是踩过了不少的坑,其中有不少都是很简单的问题,但到发现的时候都是花了不少时间, ...
- Convert Sorted Array to Binary Search Tree转换成平衡二查搜索树
Given an array where elements are sorted in ascending order, convert it to a height balanced BST. 二分 ...
- Android实战:手把手实现“捧腹网”APP(一)-----捧腹网网页分析、数据获取
Android实战:手把手实现"捧腹网"APP(一)-–捧腹网网页分析.数据获取 Android实战:手把手实现"捧腹网"APP(二)-–捧腹APP原型设计.实 ...
- 猜年龄v2.0
''' 用户登录,只有三次机会 给定年龄,用户可以猜三次年龄 年龄猜对,让用户选择两次奖励,输入无效字符,让其选择要不要礼物 用户选择两次奖励后可以退出,选择第一次后提示还有一次 ''' #基本信息定 ...