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 中,代码量极大(如果你要改板子的 ...
随机推荐
- mysql触发器使用实例
DELIMITER $$ USE `db`$$ DROP TRIGGER `member_walletinit_trigger`$$ CREATE TRIGGER `member_walletinit ...
- Android fill_parent、wrap_content和match_parent的区别
三个属性都用来适应视图的水平或垂直大小,一个以视图的内容或尺寸为基础的布局比精确地指定视图范围更加方便. 1)fill_parent 设置一个构件的布局为fill_parent将强制性地使构件扩展,以 ...
- PCA和LDA
一.PCA 在讲PCA之前,首先有人要问了,为什么我们要使用PCA,PCA到底是干什么的?这里先做一个小小的解释,举个例子:在人脸识别工作中一张人脸图像是60*60=3600维,要处理这样的数 ...
- 构建iOS稳定应用架构时方案选择的思考,主要涉及工程结构,数据流思想和代码规范
工程结构架构,减少耦合混乱以及防治需求大改造成结构重构,如何构建稳定可扩展可变换的工程结构的思考 我打算采用Information flow的方式自上而下,两大层分为基础层和展现层的结构.基础层分为多 ...
- 初学Ajax(三)
$.ajax() $.ajax()是所有ajax方法中最底层的方法,所有其他方法都是基于$.ajax()方法的封装.这个方法只有一个参数,传递一个各个功能键值对的对象. $.ajax()方法对象参数表 ...
- linux登陆欢迎提示信息的设置
Linux可以设置登录前后的欢迎信息,虽然没啥技术含量,但却是非常实用的一个小技巧. 实现登录消息的功能,可以修改3个文件. 1./etc/issue 本地登陆显示的信息,本地登录前 2./etc/i ...
- sublime3可用key
—– BEGIN LICENSE —–Nicolas HennionSingle User LicenseEA7E-8660758A01AA83 1D668D24 4484AEBC 3B04512C8 ...
- lintcode:快乐数
快乐数 写一个算法来判断一个数是不是"快乐数". 一个数是不是快乐是这么定义的:对于一个正整数,每一次将该数替换为他每个位置上的数字的平方和,然后重复这个过程直到这个数变为1,或是 ...
- hdu 4559 涂色游戏(对SG函数的深入理解,推导打SG表)
提议分析: 1 <= N <= 4747 很明显应该不会有规律的,打表发现真没有 按题意应该分成两种情况考虑,然后求其异或(SG函数性质) (1)找出单独的一个(一列中只有一个) (2)找 ...
- QT+VS编译器处理字符串时的坑真是多
以下因素都有影响:1. QT4与QT5对字符串编码的处理不同(最好不要在源代码里直接写中文,坑更多)2. QTextCodec自动对编码转换的影响3. 源代码文件对编码存储格式的不同会影响编译器对字符 ...