题意:给你个点m条边的无向图,每个节点都有一个整数权值。你的任务是执行一系列操作。操作分为3种。。。

思路:本题一点要逆向来做,正向每次如果删边,复杂度太高。逆向到一定顺序的时候添加一条边更容易。详见算法指南P235。

 #include<cstdlib>

 struct Node
{
Node *ch[]; // 左右子树
int r; // 随机优先级
int v; // 值
int s; // 结点总数
Node(int v):v(v)
{
ch[] = ch[] = NULL;
r = rand();
s = ;
}
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 ? : ); // 不要用cmp函数,因为可能会有相同结点
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);
int ret = ;
if(d == -)
{
Node* u = o;
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();
} #include<cstdio>
#include<cstring>
#include<vector>
using namespace std; const int maxc = + ;
struct Command
{
char type;
int x, p; // 根据type, p代表k或者v
} commands[maxc]; const int maxn = + ;
const int maxm = + ;
int n, m, weight[maxn], from[maxm], to[maxm], removed[maxm]; // 并查集相关
int pa[maxn];
int findset(int x)
{
return pa[x] != x ? pa[x] = findset(pa[x]) : x;
} // 名次树相关
Node* root[maxn]; // Treap int kth(Node* o, int k) // 第k大的值
{
if(o == NULL || k <= || k > o->s) 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-);
} 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* &x)
{
if(x->ch[] != NULL) removetree(x->ch[]);
if(x->ch[] != NULL) removetree(x->ch[]);
delete x;
x = NULL;
} // 主程序相关
void add_edge(int x)
{
int u = findset(from[x]), 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]);
}
}
} int query_cnt;
long long query_tot;
void query(int x, int k)
{
query_cnt++;
query_tot += kth(root[findset(x)], k);
} void change_weight(int x, int v)
{
int u = findset(x);
remove(root[u], weight[x]);
insert(root[u], v);
weight[x] = v;
} 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++) scanf("%d%d", &from[i], &to[i]);
memset(removed, , sizeof(removed)); // 读命令
int c = ;
for(;;)
{
char type;
int x, p = , v = ;
scanf(" %c", &type);
if(type == 'E') break;
scanf("%d", &x);
if(type == 'D') removed[x] = ;
if(type == 'Q') scanf("%d", &p);
if(type == 'C')
{
scanf("%d", &v);
p = weight[x];
weight[x] = v;
}
commands[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]) add_edge(i); // 反向操作
query_tot = query_cnt = ;
for(int i = c-; i >= ; i--)
{
if(commands[i].type == 'D') add_edge(commands[i].x);
if(commands[i].type == 'Q') query(commands[i].x, commands[i].p);
if(commands[i].type == 'C') change_weight(commands[i].x, commands[i].p);
}
printf("Case %d: %.6lf\n", ++kase, query_tot / (double)query_cnt);
}
return ;
}

uvalive 5031 Graph and Queries 名次树+Treap的更多相关文章

  1. UVALive 5031 Graph and Queries (Treap)

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

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

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

  3. UVaLive 5031 Graph and Queries (Treap)

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

  4. LA 5031 Graph and Queries —— Treap名次树

    离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度O(n1lo ...

  5. LA - 5031 - Graph and Queries

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

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

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

  7. UVaLive5031 Graph and Queries(时光倒流+名次树)

    题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20332 [思路] 时光倒流+名次树(rank tree). 所谓“ ...

  8. HDU 3726 Graph and Queries treap树

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

  9. Treap和名次树

    Treap名字的来源:Tree+Heap,正如名字一样,就是一颗简单的BST,一坨堆的合体.BST的不平衡的根本原因在于基于左<=根<=右的模式吃单调序列时候会无脑成长链,而Treap则添 ...

随机推荐

  1. js根据id、pid把数据转为树结构

    //格式化树数据 function toTreeData(data) { var pos = {}; var tree = []; var i = 0; while (data.length != 0 ...

  2. 深入js的面向对象学习篇(继承篇)——温故知新(三)

    写这篇有关继承的文章时,突然想起,几天前的面试.因为习惯在学习知识的时候加上自己的理解,很喜欢用自己话来解释,于是乎当面试被问起继承原理时,噼里啪啦一大堆都是自己组织的话,(也可能是因为个人紧张.外加 ...

  3. Jquery-DataTable 使用介绍

    http://dt.thxopen.com/example/server_side/simple.html

  4. highcharts 折线图

    <!doctype html> <html lang="en"> <head> <script type="text/javas ...

  5. LinkedList源代码深入剖析

    第1部分 LinkedList介绍LinkedList简介 public class LinkedList<E> extends AbstractSequentialList<E&g ...

  6. ubuntu 解决依赖问题

    安装aptitude包管理器 然后用aptitude安装 sudo aptitude install ***

  7. The Material Sourcing Process Failed To Create Picking Suggestions in INVTOTRX (文档 ID 2003806.1)

    In this Document Symptoms Cause Solution References Applies to: Oracle Inventory Management - Versio ...

  8. poj 2299 Ultra-QuickSort (归并排序 求逆序数)

    题目:http://poj.org/problem?id=2299 这个题目实际就是求逆序数,注意 long long 上白书上的模板 #include <iostream> #inclu ...

  9. C# DateDiff与DateAdd

    原文地址:http://www.wlm.so/Article/Detail/lmb49q5hxpqyi00000 刚刚在百度上搜C#里面的DateDiff,一看吓一跳,C#没有这个函数. 还有各种自定 ...

  10. RMI、RPC、SOAP通信技术介绍及比对

    1.RMI 使用java的程序员,对于RMI(RemoteMethod Invoke,远程方法调用)一定不陌生,在java中,为了在分布式应用开发时,能够方便调用远程对象,java提供了RMI的API ...