UVa 1479 (Treap 名次树) Graph and Queries
这题写起来真累。。
名次树就是多了一个附加信息记录以该节点为根的树的总结点的个数,由于BST的性质再根据这个附加信息,我们可以很容易找到这棵树中第k大的值是多少。
所以在这道题中用一棵名次树来维护一个连通分量。
由于图中添边比较方便,用并查集来表示连通分量就好了,但是删边不太容易实现。
所以,先把所有的边删去,然后逆序执行命令。当然,C命令也要发生一些变化,比如说顺序的情况是从a变成b,那么逆序执行的话应该就是从b变成a。
最后两棵树的合并就是启发式合并,把节点数少的数并到节点数多的数里去。
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std; struct Node
{
Node* ch[];
int r, v, s;
Node(int v):v(v) { ch[] = ch[] = NULL; s = ; r = rand(); }
int cmp(int x)
{
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);
if(d == -)
{
if(o->ch[] == NULL) o = o->ch[];
else if(o->ch[] == NULL) o = o->ch[];
else
{
int d2 = o->ch[]->r < o->ch[]->r ? : ;
rotate(o, d2); remove(o->ch[d2], x);
}
}
else remove(o->ch[d], x);
if(o != NULL) o->maintain();
} const int maxc = + ;
struct Command
{
char type;
int x, p;
}cmd[maxc]; const int maxn = + ;
const int maxm = + ; int weight[maxn], from[maxm], to[maxm];
bool removed[maxm];
int n, m, query_cnt;
long long query_tot; int pa[maxn];
int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); } Node* root[maxn]; int kth(Node* o, int k)
{
if(o == NULL || k <= || k > o->s) return ;
int s = o->ch[] == NULL ? : o->ch[]->s;
if(k == s + ) return o->v;
if(k <= s) return kth(o->ch[], k);
return kth(o->ch[], k - s - );
} void MergeTo(Node* &src, Node* &dest)
{
if(src->ch[] != NULL) MergeTo(src->ch[], dest);
if(src->ch[] != NULL) MergeTo(src->ch[], dest);
insert(dest, src->v);
delete src;
src = NULL;
} void RemoveTree(Node* &o)
{
if(o->ch[] != NULL) RemoveTree(o->ch[]);
if(o->ch[] != NULL) RemoveTree(o->ch[]);
delete o;
o = NULL;
} void AddEdge(int x)
{
int u = findset(from[x]);
int v = findset(to[x]);
if(u != v)
{
if(root[u]->s < root[v]->s) { pa[u] = v; MergeTo(root[u], root[v]); }
else { pa[v] = u; MergeTo(root[v], root[u]); }
}
} void Query(int x, int k)
{
query_cnt++;
query_tot += kth(root[findset(x)], k);
} void ChangeWeight(int x, int v)
{
int u = findset(x);
remove(root[u], weight[x]);
insert(root[u], v);
weight[x] = v;
} int main()
{
//freopen("in.txt", "r", stdin); 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++) scanf("%d%d", &from[i], &to[i]);
memset(removed, false, sizeof(removed)); int c = ;
for(;;)
{
char type[]; scanf("%s", type);
if(type[] == 'E') break;
int x, p = , v = ;
scanf("%d", &x);
if(type[] == 'D') removed[x] = true;
if(type[] == 'Q') scanf("%d", &p);
if(type[] == 'C')
{
scanf("%d", &v);
p = weight[x];
weight[x] = v;
}
cmd[c++] = (Command) { type[], x, p };
} for(int i = ; i <= n; i++)
{
pa[i] = i; if(root[i] != NULL) RemoveTree(root[i]);
root[i] = new Node(weight[i]);
}
for(int i = ; i <= m; i++) if(!removed[i]) AddEdge(i); query_cnt = query_tot = ;
for(int i = c - ; i >= ; i--)
{
char type = cmd[i].type;
int x = cmd[i].x, p = cmd[i].p;
if(type == 'D') AddEdge(x);
if(type == 'Q') Query(x, p);
if(type == 'C') ChangeWeight(x, p);
}
printf("Case %d: %.6f\n", ++kase, query_tot / (double)query_cnt);
} return ;
}
代码君
UVa 1479 (Treap 名次树) Graph and Queries的更多相关文章
- LA 5031 Graph and Queries —— Treap名次树
离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度O(n1lo ...
- 「模板」「讲解」Treap名次树
Treap实现名次树 前言 学平衡树的过程可以说是相当艰难.浏览Blog的过程中看到大量指针版平衡树,不擅长指针操作的我已经接近崩溃.于是,我想着一定要写一篇非指针实现的Treap的Blog. 具体如 ...
- POJ-1442 Black Box,treap名次树!
Black Box 唉,一天几乎就只做了这道题,成就感颇低啊! 题意:有一系列插入查找操作,插入每次 ...
- [la P5031&hdu P3726] Graph and Queries
[la P5031&hdu P3726] Graph and Queries Time Limit: 10000/5000 MS (Java/Others) Memory Limit: ...
- uvalive 5031 Graph and Queries 名次树+Treap
题意:给你个点m条边的无向图,每个节点都有一个整数权值.你的任务是执行一系列操作.操作分为3种... 思路:本题一点要逆向来做,正向每次如果删边,复杂度太高.逆向到一定顺序的时候添加一条边更容易.详见 ...
- UVaLive5031 Graph and Queries(时光倒流+名次树)
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20332 [思路] 时光倒流+名次树(rank tree). 所谓“ ...
- HDU 3726 Graph and Queries treap树
题目来源:HDU 3726 Graph and Queries 题意:见白书 思路:刚学treap 參考白皮书 #include <cstdio> #include <cstring ...
- Treap和名次树
Treap名字的来源:Tree+Heap,正如名字一样,就是一颗简单的BST,一坨堆的合体.BST的不平衡的根本原因在于基于左<=根<=右的模式吃单调序列时候会无脑成长链,而Treap则添 ...
- Treap 实现名次树
在主流STL版本中,set,map,都是BST实现的,具体来说是一种称为红黑树的动态平衡BST: 但是在竞赛中并不常用,因为红黑树过于复杂,他的插入 5 种,删除 6 中,代码量极大(如果你要改板子的 ...
随机推荐
- Sqli-labs less 17
Less-17 本关我们可以看到是一个修改密码的过程,利用的是update语句,与在用select时是一样的,我们仅需要将原先的闭合,构造自己的payload. 尝试报错 Username:admin ...
- Java 8怎么了之二:函数和原语
[编者按]本文作者为专注于自然语言处理多年的 Pierre-Yves Saumont,Pierre-Yves 著有30多本主讲 Java 软件开发的书籍,自2008开始供职于 Alcatel-Luce ...
- DotNetBar之SupergridControl显示图片,行距自动调整
DotNetBar第三方控件SupergridControl中显示图片 1.设置SuperGridControl: SuperGridControl的GridColumn的EditorType设置为: ...
- javascript和“主流大型语言”(c# JAVA C++等)的差异
1.javascript不支持overload,因为它的函数参数是以数组方式来实现的,没有固定的参数签名,所以无法重载. 2.javascript的基本类型只有5个:number string boo ...
- Linux Rsync
一.Rsync介绍 1.什么是Rsync Rsync 即Remote Rynchronization,是一款开源的.快速的.多功能的.可实现全量或增量的本地或者远程数据镜像同步复制.备份的优秀工具. ...
- Freebie: Material Design UI Kit
点这里 Following the guidelines laid out by Google, this free UI kit has been designed so that you can ...
- facebook design question 总结
http://blog.csdn.net/sigh1988/article/details/9790337 这里原帖地址: http://www.mitbbs.com/article_t/JobHun ...
- STL中set底层实现方式
Q:STL中set底层实现方式? 为什么不用hash? A: 第一个问题:set底层实现方式为RB树(即红黑树). 第二个问题: 首先set,不像map那样是key-value对,它的key与valu ...
- java 哪些情况下会使对象锁释放
Java_多线程_锁释放 问:Java多线程运行环境中,在哪些情况下会使对象锁释放?答:由于等待一个锁的线程只有在获得这把锁之后,才能恢复运行,所以让持有锁的线程在不再需要锁的时候及时释放锁是很重要的 ...
- BeanUtils.copyProperties与PropertyUtils.copyProperties用法及区别
一.简介: BeanUtils提供对Java反射和自省API的包装.其主要目的是利用反射机制对JavaBean的属性进行处理.我们知道,一个JavaBean 通常包含了大量的属性,很多情况下,对Jav ...