题意
给出一个无向图,每次有三个操作
操作 | 描述 |
---|---|
D X | 删除ID为X的边。输入保证每条边至多被删除一次 |
Q X k | 计算与结点X连通的结点中(包括本身)第k大的权值,如果不存在,直接输出0 |
C X V | 把结点X的权值改为V |
题解
采用离线的方式,从后往前做,把删除边当做是添加边。用平衡树Treap维护这些操作,添加边相当于树合并,修改权值可以用删除+插入来完成,需要注意的是这里的修改权值应该改成之前的值,因为离线的方法是从后往前做的。判断是否在同一个连通集内用并查集维护即可。
新建每一颗Treap的时候一定要在新图建号之后再New Node,否则权值并不是最新的权值。
具体代码
#include <iostream>
#include <cstdio>
#include <assert.h>
using namespace std;
const int maxn = 2e4+5;
int n,m,weight[maxn],qry;
int cnt = 0;
long long ans;
// Treap
struct Node{
int v;
int s;
int r;
Node* ch[2];
Node(int v): v(v) {
ch[0] = ch[1] = NULL;
r = rand();
s = 1;
}
int cmp(int x) const {
if(x == v) return -1;
return x < v ? 0:1;
}
void maintain() {
s = 1;
if(ch[0] != NULL) s += ch[0]->s;
if(ch[1] != NULL) s += ch[1]->s;
}
};
Node* root[maxn];
// 旋转, d = 0左旋, d = 1右旋
void rotate(Node* &o, int d) {
Node* k = o->ch[d^1];
o->ch[d^1] = 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 ? 0:1;
insert(o->ch[d],x);
if(o->ch[d]->r > o->r)
rotate(o,d^1);
}
o->maintain();
}
// 删除
void remove(Node* &o, int x) {
int d = o->cmp(x);
if(d == -1) {
Node* u = o;
if(o->ch[0] != NULL && o->ch[1] != NULL) {
int d2 = o->ch[0]->r > o->ch[1]->r ? 1 : 0;
rotate(o,d2);
remove(o->ch[d2],x);
}
else {
if(o->ch[0] != NULL) {
o = o->ch[0];
}
else
o = o->ch[1];
delete u;
}
}
else {
remove(o->ch[d],x);
}
if(o != NULL)
o->maintain();
}
// 第k大的值
int kth(Node* o, int k) {
if(o == NULL || k <= 0 || k > o->s)
return 0;
int s = (o->ch[1] == NULL)?0:(o->ch[1]->s);
if(k == s+1) return o->v;
if(k < s+1)
return kth(o->ch[1], k);
else
return kth(o->ch[0], k-s-1);
}
// 合并Treap
void mergeTo(Node* &src, Node* &dest) {
if(src->ch[0] != NULL) mergeTo(src->ch[0], dest);
if(src->ch[1] != NULL) mergeTo(src->ch[1], dest);
insert(dest, src->v);
delete src;
src = NULL;
}
void removeTree(Node* &x) {
if(x->ch[0] != NULL) removeTree(x->ch[0]);
if(x->ch[1] != NULL) removeTree(x->ch[1]);
delete x;
x = NULL;
}
//并查集
int fa[maxn];
void init(int x) {
for(int i = 0; i <= x; ++i) {
fa[i] = i;
if(root[i] != NULL)
removeTree(root[i]);
root[i] = new Node(weight[i]);
}
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
const int maxc = 5e5+5;
struct Edge{
int u,v;
bool flag;
}edge[maxn*3];
struct Command{
char op;
int x;
int val;
} command[maxc];
//小的合并到大的上
void add_node(int i) {
int u = find(edge[i].u), v = find(edge[i].v);
if(u != v) {
if(root[u]->s > root[v]->s) {
fa[v] = u;
mergeTo(root[v], root[u]);
}
else {
fa[u] = v;
mergeTo(root[u], root[v]);
}
}
}
void solve() {
for(int i = 1; i <= m; ++i) {
if(edge[i].flag)
add_node(i);
}
for(int i = cnt; i >= 1; --i) {
char op = command[i].op;
if(op == 'D') {
int x = command[i].x;
edge[x].flag = true;
add_node(x);
}
else if(op == 'Q') {
qry++;
int x = command[i].x;
int k = command[i].val;
x = find(x);
ans += kth(root[x], k);
// cout << ans << endl;
}
else if(op == 'C') {
int x = command[i].x;
int v = command[i].val;
x = find(x);
remove(root[x], weight[command[i].x]);
insert(root[x], v);
weight[command[i].x] = v;
}
}
}
int main() {
// freopen("in.txt", "r", stdin);
int kase = 1;
while(scanf("%d%d", &n, &m)==2 && n) {
ans = 0;
qry = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", &weight[i]);
}
for(int i = 1; i <= m; ++i) {
scanf("%d%d", &edge[i].u, &edge[i].v);
edge[i].flag = true;
}
char op;
cnt = 0;
while(scanf(" %c", &op)) {
if(op == 'E')
break;
int x, val;
scanf("%d", &x);
if(op != 'D') {
scanf("%d", &val);
if(op == 'C') {
int p = weight[x];
weight[x] = val;
val = p;
}
}
else
edge[x].flag = false;
command[++cnt].op = op;
command[cnt].x = x;
command[cnt].val = val;
}
init(n);
solve();
printf("Case %d: %.6lf\n", kase++, ans/(double)qry);
}
return 0;
}