离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度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名次树的更多相关文章

  1. LA - 5031 - Graph and Queries

    题意:一个N个点(编号从1开始),M条边的无向图(编号从1开始),有3种操作: D X:把编号为X的边删了: Q X K:查询编号为X的结点所在连通分量第K大的元素: C X V:将编号为X的结点的权 ...

  2. UVaLive 5031 Graph and Queries (Treap)

    题意:初始时给出一个图,每个点有一个权值,三种操作:(1)删除某个边:(2)修改每个点的权值:(3)询问与节点x在一个连通分量中所有点的第K大的权值. 析:首先是要先离线,然后再倒着做,第一个操作就成 ...

  3. uvalive 5031 Graph and Queries 名次树+Treap

    题意:给你个点m条边的无向图,每个节点都有一个整数权值.你的任务是执行一系列操作.操作分为3种... 思路:本题一点要逆向来做,正向每次如果删边,复杂度太高.逆向到一定顺序的时候添加一条边更容易.详见 ...

  4. UVa 1479 (Treap 名次树) Graph and Queries

    这题写起来真累.. 名次树就是多了一个附加信息记录以该节点为根的树的总结点的个数,由于BST的性质再根据这个附加信息,我们可以很容易找到这棵树中第k大的值是多少. 所以在这道题中用一棵名次树来维护一个 ...

  5. HDU 3726 Graph and Queries treap树

    题目来源:HDU 3726 Graph and Queries 题意:见白书 思路:刚学treap 參考白皮书 #include <cstdio> #include <cstring ...

  6. 「模板」「讲解」Treap名次树

    Treap实现名次树 前言 学平衡树的过程可以说是相当艰难.浏览Blog的过程中看到大量指针版平衡树,不擅长指针操作的我已经接近崩溃.于是,我想着一定要写一篇非指针实现的Treap的Blog. 具体如 ...

  7. UVALive 5031 Graph and Queries (Treap)

    删除边的操作不容易实现,那么就先离线然后逆序来做. 逆序就变成了合并,用并存集判断连通,用Treap树来维护一个连通分量里的名次. Treap = Tree + Heap.用一个随机的优先级来平衡搜索 ...

  8. UVALive - 5031 Graph and Queries (并查集+平衡树/线段树)

    给定一个图,支持三种操作: 1.删除一条边 2.查询与x结点相连的第k大的结点 3.修改x结点的权值 解法:离线倒序操作,平衡树or线段树维护连通块中的所有结点信息,加个合并操作就行了. 感觉线段树要 ...

  9. UVALive5031 Graph and Queries(Treap)

    反向操作,先求出最终状态,再反向操作. 然后就是Treap 的合并,求第K大值. #include<cstdio> #include<iostream> #include< ...

随机推荐

  1. PHP生成短连接的方法

    PHP生成短连接的方法.md PHP生成短连接的方法 直接贴上方法,函数可以查看手册. <?php /** 生成短网址 * @param String $url 原网址 * @return St ...

  2. PHPCMS 按点击量排序

    {pc:content action="hits" catid="12" num="4" order="views DESC&qu ...

  3. PHP 中的 curl 函数发送 Post 请求应该注意的几点

    public function http_request( $url, $post = '', $timeout = 5 ){ if( empty( $url ) ){return ;}$ch = c ...

  4. RapidMiner Studio 入门

    http://docs.rapidminer.com/studio/getting-started/ RapidMiner Studio 入门 FEIFEI MA 2015-12-07RAPIDMIN ...

  5. DELL(linux 系统里系统掉盘)(阵列Foreign)命令行里重做阵列

    故障现象 (阵列Foreign 系统下重做阵列) 例:四合一机器(DELL_XENCOMPA09) 有四个硬盘 如图 少认到一个 df  -h fdisk -l 2>/dev/null | gr ...

  6. oracle管理索引

    索引是用于加速数据存取的数据对象,合理的使用索引可以大大降低I/O次数,从而提高数据访问性能.索引有很多种我们主要介绍常用的几种: 为什么添加了索引或,会加快查询速度呢? n  单列索引 单列索引是基 ...

  7. 【JZOJ4860】【NOIP2016提高A组集训第7场11.4】分解数

    题目描述 Dpstr学习了动态规划的技巧以后,对数的分解问题十分感兴趣. Dpstr用此过程将一个正整数x分解成若干个数的乘积:一开始令集合A中只有一个元素x,每次分解时从A中取一个元素a并找出两个大 ...

  8. 在laravel框架中使用ajax请求报错419

    laravel框架中报419 419 unknown status 这个时候你需要将这个接口放到api路由上,这样可以跳过CSRF的检查

  9. 【C++】STL :栈

    c++stack(堆栈)是一个容器的改编,它实现了一个先进后出的数据结构(FILO) 使用该容器时需要包含#include<stack>头文件: 定义stack对象的示例代码如下: sta ...

  10. 64位Linux编译C代码,crt1.o文件格式不对的问题

    今天在某台64位LInux下编译一个简单的hello world的C程序,报错: /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../crt1.o: cou ...