UVaLive5031 Graph and Queries(时光倒流+名次树)
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20332
【思路】
时光倒流+名次树(rank tree)。
所谓“时光倒流”即逆向处理,因为D删除边并不好操作所以我们倒着处理,删除边转化为添加边,C转化为将weight变回操作前的数,Q不变。
名次树实现以上操作:名次树是Treap+s域实现的,可以提供kth即查询第k大的数的操作和Treap的所有功能。
1)对于D(x):合并from[x]与to[x]所在的rank tree,后序思想,将src中的结点逐个添加到dest中,采用启发式合并。
2)对于Q(x,k):调用kth操作同时累计cnt与tot。
3)对于C(x,v):调用一次romove(root[findset(x)],weight[x]),再调用一次insert(root[findset(x)],v)。
用到一个并查集快速寻找x所属rank tree的根。
【代码】
#include<cstdio>
#include<ctime>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<iostream>
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std; const int maxn = +;
//Treap相关
struct Node{
Node *ch[];
int r,v,s; //r为优先级 v为键值 s为结点总数
Node(int w) :v(w) { ch[]=ch[]=NULL; s=; r=rand(); }
int cmp(int x) const{ //x应在左子树d=0 x应在右子树d=1
if(x==v) return -;
return x<v? :;
}
int cmp2(int x) const{ return x<v? :; }
void maintain() { //名次树维护 s
s=;
if(ch[]!=NULL) s+=ch[]->s;
if(ch[]!=NULL) s+=ch[]->s;
}
};
void rotate(Node* &o,int d) { //旋转操作 d=0左旋d=1右旋
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=o->cmp2(x); //可能会有键值相等的结点
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==-) {
Node *u=o;
if(o->ch[]!=NULL && o->ch[]!=NULL){ //根据左右子[优先级]旋转 旋转后递归删除x
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) { //返回第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* &o) { //[后序遍历]删除一棵名次树
if(o->ch[]!=NULL) removetree(o->ch[]);
if(o->ch[]!=NULL) removetree(o->ch[]);
delete o;
o=NULL;
}
//并查集相关
int pa[maxn];
int findset(int u) { return u==pa[u]? u:pa[u]=findset(pa[u]); }
//题目相关
Node* root[maxn];
int weight[maxn],from[maxn],to[maxn];
bool removed[maxn];
int n,m,cnt,kase;
long long sum;
struct Command{ char type; int a,b;
};
vector<Command> cs; void addedge(int i) {
int u=findset(from[i]),v=findset(to[i]);
if(u!=v) {
if(root[u]->s > root[v]->s) { pa[v]=u; mergeto(root[v],root[u]); }
else { pa[u]=v; mergeto(root[u],root[v]); }
}
}
void query(int x,int k) {
cnt++;
sum+=kth(root[findset(x)],k);
}
void changeWeight(int x,int p) {
int u=findset(x);
remove(root[u],weight[x]);
insert(root[u],p);
weight[x]=p;
} int main() {
srand(time()); //初始化随机数种子
kase=;
while(scanf("%d%d",&n,&m)== && (n&&m)) {
FOR(i,,n) scanf("%d",&weight[i]);
FOR(i,,m) scanf("%d%d",&from[i],&to[i]);
char type;
memset(removed,,sizeof(removed));
cs.clear();
while(scanf(" %c",&type)== && type!='E') {
int x=,p=;
scanf("%d",&x);
if(type=='D') removed[x]=;
if(type=='Q') scanf("%d",&p);
if(type=='C') {
scanf("%d",&p);
swap(p,weight[x]);
}
cs.push_back((Command){type,x,p});
}
FOR(i,,n) {
pa[i]=i; if(root[i]!=NULL) removetree(root[i]);
root[i]=new Node(weight[i]);
}
FOR(i,,m) if(!removed[i]) addedge(i);
int d=cs.size(); sum=cnt=;
for(int i=d-;i>=;i--) {
if(cs[i].type=='D') addedge(cs[i].a);
if(cs[i].type=='Q') query(cs[i].a,cs[i].b);
if(cs[i].type=='C') changeWeight(cs[i].a,cs[i].b);
}
printf("Case %d: %.6lf\n",++kase,sum/(double)cnt);
}
return ;
}
UVaLive5031 Graph and Queries(时光倒流+名次树)的更多相关文章
- UVALive5031 Graph and Queries(Treap)
反向操作,先求出最终状态,再反向操作. 然后就是Treap 的合并,求第K大值. #include<cstdio> #include<iostream> #include< ...
- UVa 1479 (Treap 名次树) Graph and Queries
这题写起来真累.. 名次树就是多了一个附加信息记录以该节点为根的树的总结点的个数,由于BST的性质再根据这个附加信息,我们可以很容易找到这棵树中第k大的值是多少. 所以在这道题中用一棵名次树来维护一个 ...
- uvalive 5031 Graph and Queries 名次树+Treap
题意:给你个点m条边的无向图,每个节点都有一个整数权值.你的任务是执行一系列操作.操作分为3种... 思路:本题一点要逆向来做,正向每次如果删边,复杂度太高.逆向到一定顺序的时候添加一条边更容易.详见 ...
- LA 5031 Graph and Queries —— Treap名次树
离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度O(n1lo ...
- HDU 3726 Graph and Queries treap树
题目来源:HDU 3726 Graph and Queries 题意:见白书 思路:刚学treap 參考白皮书 #include <cstdio> #include <cstring ...
- [la P5031&hdu P3726] Graph and Queries
[la P5031&hdu P3726] Graph and Queries Time Limit: 10000/5000 MS (Java/Others) Memory Limit: ...
- HDU 3726 Graph and Queries 平衡树+前向星+并查集+离线操作+逆向思维 数据结构大综合题
Graph and Queries Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
- 时光倒流程序设计-AlloyTicker
熵与负熵 熵遵循熵增原理,即无序非热能与热能之间的转换具有方向性.薛定谔说过:生命本质在于负熵.熵代表的是无序,负熵就是熵的对立,而负熵表示的则是有序.汲取负熵(米饭.面包.牛奶.鸡蛋),可以简单的理 ...
- Treap和名次树
Treap名字的来源:Tree+Heap,正如名字一样,就是一颗简单的BST,一坨堆的合体.BST的不平衡的根本原因在于基于左<=根<=右的模式吃单调序列时候会无脑成长链,而Treap则添 ...
随机推荐
- vim字符串替换
vi/vim 中可以使用 :s 命令来替换字符串.以前只会使用一种格式来全文替换,今天发现该命令有很多种写法(vi 真是强大啊,还有很多需要学习),记录几种在此,方便以后查询. :s/vivian/s ...
- mysql 远程访问 配置
sudo vi /etc/mysql/my.cnf 找到bind-address = 127.0.0.1 注释掉这行:#bind-address = 127.0.0.1 或者改为: bind-addr ...
- oracle 中查看一张表是否有主键,主键在哪个字段上的语句怎么查如要查aa表,
select a.constraint_name, a.column_name from user_cons_columns a, user_constraints b where a.constra ...
- Xcode添加静态库以及编译选项配置常见问题
一,Xcode编译出现Link错误,出现"duplicate symbols for architecture i386 clang"提示.问题:链接时,项目有重名文件.解决:根据 ...
- UIViewAnimationOptions类型
一个非常强大的博客 http://www.cnblogs.com/kenshincui/ 像我这种新手确实应该多看看 常规动画属性设置(可以同时选择多个进行设置) UIViewAnimati ...
- jQuery 效果 - 淡入淡出
通过 jQuery,您可以实现元素的淡入淡出效果. 点击展示 淡入/淡出 面板 实例 jQuery fadeIn()演示 jQuery fadeIn() 方法. jQuery fadeOut()演示 ...
- javascript——面向对象程序设计(1)
<script type="text/javascript"> //ECMA-262把对象定义为:“无序属性的 集合,其属性可以包含基本值.对象或者函数” //理解对象 ...
- 读终端输入数据BufferedReader
public static void main(String[] args) { BufferedReader br=new BufferedReader(new InputStream ...
- Android 学习手札(二) 活动(Activity)组件
1.建立和配置Activity 建立Android工程时已经自动生成了一个默认的Activity,同时也生成了很多与Activity相关的文件,例如,res目录中的XML及图像文件.AndroidMa ...
- javascript为目标位置div等设置高度
应该是DOM的东西: document.getElementById("目标id").style.height = 多高(数值)+"px";