题意:给你个点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. iOS socket编程 第三方库 AsyncSocket(GCDAsyncSocket)

    Socket描述了一个IP.端口对.它简化了程序员的操作,知道对方的IP以及PORT就可以给对方发送消息,再由服务器端来处理发送的这些消息.所以,Socket一定包含了通信的双发,即客户端(Clien ...

  2. 【C++基础】 各种“虚”总结(ing...)

    虚基类,虚函数,虚析构函数,纯虚函数,虚函数表(待补充) 一.虚基类——在继承方式(public / private)之前加 virtual class B1: virtual public B0{} ...

  3. ***CI异常记录到日志:CodeIgniter中设计一个全局exception hook

    在CodeIgniter中,当发生异常时,经常要通知系统管理员,因此有必要在全局的高度上 捕捉异常,因此可以写一个hook, 比如在config目录的hook.php中,加入: $hook['pre_ ...

  4. Android ListView相关 fastScrollEnabled

    1.android:fastScrollEnabled="true" 当数据量比较多的时候,右侧会出现一个可以拉动的滚动条,这样可以很快的拉动,如图:

  5. linux查看磁盘使用情况

    # 查看磁盘使用情况 $ df -l # 查看某个目录在哪个分区,比如查看/root文件夹在哪个分区 $ df /root # 查看linux系统具体分区情况 $ fdisk -l

  6. FF浏览器来帮助我们录制脚本

    有时我们录制一个页面的脚本,我们需要知道这个页面哪些请求是耗时最大的?这个时候FF浏览器的网络分析功能就可以派上用场了,打开火狐浏览器按F12: 点击重新载入,可以看到下面的信息: 看到最耗时的操作了 ...

  7. 李洪强iOS开发之自定义cell的使用

    第一步: 创建自定义cell类,继承自UItableVIewcell 第二步: 在sb中布局自己需要的视图控件并且将此cell与我刚刚创建的cell类进行关联.并且连线  第三步: 创建modle类, ...

  8. 常用的富文本框插件FreeTextBox、CuteEditor、CKEditor、FCKEditor、TinyMCE、KindEditor ;和CKEditor实例

    http://www.cnblogs.com/cxd4321/archive/2013/01/30/2883078.html 目前市面上用的比较多的富文本编辑器有: FreeTextBox 一个有很多 ...

  9. 【Web】CDN加速效果浅析

    1. 什么是CDN? CDN的全称是Content Delivery Network,即内容分发网络.其目的是通过在现有的Internet中增加一层新的CACHE(缓存)层,将网站的内容发布到最接近用 ...

  10. mysql concat和group_concat

    mysql concat(str1,str2...)连接两个字符串,(数字也是可以的,会转成字符串) MySQL的concat函数在连接字符串的时候,只要其中一个是NULL,那么将返回NULL mys ...