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则添 ...
随机推荐
- 通过注册表查找oracle_home的位置
运行regedit进入注册表 ctrl+f查找oracle_home
- Javascript和ECMAScript二三事
来自<javascript高级程序设计 第三版:作者Nicholas C. Zakas>的学习笔记(一) Javascript是一种专为与网页交互而设计的脚本语言,由下列三个不同部分组成: ...
- PAT-乙级-1011. A+B和C (15)
1011. A+B和C (15) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 HOU, Qiming 给定区间[-231, 231 ...
- Cloud Insight 和 BearyChat 第一次合体,好紧张!
说到 ChatOps 我们可能立刻想到是 Slack(啥?没听过?哦!),但是由于国内网络和语言的问题你可能无法拥有很好的体验了.那就把目光转回国内吧,国内的话就不得不提到 BearyChat 等 C ...
- Java的ResultSet中rs.next()含义
- PLS-00215:字符串长度限制在范围
在Oracle中有一张people表 创建跟新表的存储过程 修改定义字段长度 总结:在Oracle中执行存储过程时,输出参数的长度要与原表中字段长度一致!
- ScrollView can host only one direct child 解决
主要是ScrollView内部只能有一个子元素,即不能并列两个子元素,所以需要把所有的子元素放到一个LinearLayout内部或RelativeLayout等其他布局方式让后再在这个layout外部 ...
- Yii cookie操作
设置cookie: $cookie = new CHttpCookie('mycookie','this is my cookie'); $cookie->expire = time()+60* ...
- ubuntu终端颜色配置
对于刚接触ubuntu的同学们,打开终端(ctrl+alt+T),会发现里面都是一个颜色,不管是用户名.主机名还是命令都是白色,当然,用 ls 列出文件的时候是会多一种颜色的.即使这样,对开发人员来说 ...
- C++ 类的内存分布
C++类内存分布 转自:http://www.cnblogs.com/jerry19880126/p/3616999.html 先写下总结,通过总结下面的例子,你就会明白总结了. 下面总结一下: ...