uvalive 5031 Graph and Queries 名次树+Treap
题意:给你个点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的更多相关文章
- UVALive 5031 Graph and Queries (Treap)
删除边的操作不容易实现,那么就先离线然后逆序来做. 逆序就变成了合并,用并存集判断连通,用Treap树来维护一个连通分量里的名次. Treap = Tree + Heap.用一个随机的优先级来平衡搜索 ...
- UVALive - 5031 Graph and Queries (并查集+平衡树/线段树)
给定一个图,支持三种操作: 1.删除一条边 2.查询与x结点相连的第k大的结点 3.修改x结点的权值 解法:离线倒序操作,平衡树or线段树维护连通块中的所有结点信息,加个合并操作就行了. 感觉线段树要 ...
- UVaLive 5031 Graph and Queries (Treap)
题意:初始时给出一个图,每个点有一个权值,三种操作:(1)删除某个边:(2)修改每个点的权值:(3)询问与节点x在一个连通分量中所有点的第K大的权值. 析:首先是要先离线,然后再倒着做,第一个操作就成 ...
- LA 5031 Graph and Queries —— Treap名次树
离线做法,逆序执行操作,那么原本的删除边的操作变为加入边的操作,用名次树维护每一个连通分量的名次,加边操作即是连通分量合并操作,每次将结点数小的子树向结点数大的子树合并,那么单次合并复杂度O(n1lo ...
- LA - 5031 - Graph and Queries
题意:一个N个点(编号从1开始),M条边的无向图(编号从1开始),有3种操作: D X:把编号为X的边删了: Q X K:查询编号为X的结点所在连通分量第K大的元素: C X V:将编号为X的结点的权 ...
- UVa 1479 (Treap 名次树) Graph and Queries
这题写起来真累.. 名次树就是多了一个附加信息记录以该节点为根的树的总结点的个数,由于BST的性质再根据这个附加信息,我们可以很容易找到这棵树中第k大的值是多少. 所以在这道题中用一棵名次树来维护一个 ...
- 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则添 ...
随机推荐
- flex toolTip样式设置
需要3个文件.一个是样式类,一个样式文件,一个是mxml文件. ●MyToolTip.as package{ import mx.core.UITextField; import mx.ski ...
- OpenSessionInViewFilter 的配置及替代方案(转)
鸣谢:http://justsee.iteye.com/blog/1174999,http://blog.csdn.net/sunsea08/article/details/4545186 Sprin ...
- 增强LSH
通过LSH hash functions我们能够得到一个或多个hash table,每个桶内的数据之间是近邻的可能性很大.我们希望原本相邻的数据经过LSH hash后,都能够落入到相同的桶内,而不相邻 ...
- 团体程序设计天梯赛-练习集L2-005. 集合相似度
L2-005. 集合相似度 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 给定两个整数集合,它们的相似度定义为:Nc/Nt*1 ...
- vs2015 打不开了 提示"CSharpPackage",未能正确加载xx包
原文:vs2015 打不开了 提示"CSharpPackage" 最近发现vs2015 在新建项目和加载现有项目的时候会报错 提示 开始我以为是系统的问题导致vs 配置除了问题,重 ...
- windows 下 文件属性及目录列表操作
转:http://blog.sina.com.cn/s/blog_686d0fb001012tsg.html 我们需要一个结构体和几个函数.这些函数和结构体在<io.h>的头文件中,结构体 ...
- POJ3026——Borg Maze(BFS+最小生成树)
Borg Maze DescriptionThe Borg is an immensely powerful race of enhanced humanoids from the delta qua ...
- Python之异常篇 [待更新]
简介 当你的程序中出现某些 异常的 状况的时候,异常就发生了.例如,当你想要读某个文件的时候,而那个文件不存在.或者在程序运行的时候,你不小心把它删除了.上述这些情况可以使用异常来处理. 假如你的程序 ...
- laravel Authentication and Security
Creating the user modelFirst of all, we need to define the model that is going to be used to represe ...
- XMPP聊天客户端环境搭建
1.服务器选择:ejabberd,具体安装过程请参考:http://blog.csdn.net/linhanmin/article/details/9876819 2.客户端配置: 采用xmppfra ...