这题写起来真累。。
名次树就是多了一个附加信息记录以该节点为根的树的总结点的个数,由于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 中,代码量极大(如果你要改板子的 ...
随机推荐
-
Java中的多线程你只要看这一篇就够了
学习Java的同学注意了!!! 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:279558494 我们一起学Java! 引 如果对什么是线程.什么是进程仍存有疑惑, ...
-
iOS开发-Alpha,Hidden与Opaque区别
UIView中的这三个属性用的比较多,尤其是Alpha和Opaque之间有的时候不是很好分别,稍微整理下: Alpha(不透明度) alpha是不透明度,属性为浮点类型的值,取值范围从0到1.0,表示 ...
-
.NET GC Server-Background-GC
Garbage Collection and Performancehttps://msdn.microsoft.com/en-us/library/ee851764(v=vs.110).aspx h ...
-
Hive[2] 基础介绍
2.3 Hive 内部介绍: P44 $HIVE_HOME/lib 下的 jar 文件是具体的功能部分:(CLI模块) 其它组件,Thrift 服务,可以远程访问其他进程功能:也有使用 JDBC 和 ...
-
C#正则表达式Regex类使用
作为文本处理的利器——Perl语言对正则表达式的最强大支持起到了重要的作用,正因为如此,许多其他语言在加入正则表达式引擎的时候都会或多或少的兼顾perl风格的正则表达式,开发出相应的引擎.本人使用pe ...
-
让Hibernate生成的DDL脚本自动增加注释
我们知道可以通过Hibernate对象自动生成DDL建表语句,通过PowerDesigner工具可以反向工程生成数据字典,但是在生成的DDL中一直不能写上中文的注释,这就使我们生成的数据字典不具有可用 ...
-
jQuery常用及基础知识总结(三)
1.通过jquery的$()引用元素包括通过id.class.元素名以及元素的层级关系及dom或者xpath条件等方法,且返回的对象为jquery对象(集合对象),不能直接调用dom定义的方法. 2. ...
-
Java中三种代理模式
代理模式 代理(Proxy)是一种设计模式,提供了间接对目标对象进行访问的方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的功能上,增加额外的功能补充,即扩展目标对象的功能. 这就 ...
-
L - LCM Walk HDU - 5584 (数论)
题目链接: L - LCM Walk HDU - 5584 题目大意:首先是T组测试样例,然后给你x和y,这个指的是终点.然后问你有多少个起点能走到这个x和y.每一次走的规则是(m1,m2)到(m1+ ...
-
Oracle PL/SQL,游标,过程
1.PL/SQL 语法相关 -- SQL 语言只是访问,操作数据库的语言,而比并不是程序设计语言,因此不能用于程序开发. -- PL/SQL 是在标准SQl语言上进行过程性扩展后形成的程序设计语言, ...