这题写起来真累。。

名次树就是多了一个附加信息记录以该节点为根的树的总结点的个数,由于BST的性质再根据这个附加信息,我们可以很容易找到这棵树中第k大的值是多少。

所以在这道题中用一棵名次树来维护一个连通分量。

由于图中添边比较方便,用并查集来表示连通分量就好了,但是删边不太容易实现。

所以,先把所有的边删去,然后逆序执行命令。当然,C命令也要发生一些变化,比如说顺序的情况是从a变成b,那么逆序执行的话应该就是从b变成a。

最后两棵树的合并就是启发式合并,把节点数少的数并到节点数多的数里去。

 #include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std; struct Node
{
Node* ch[];
int r, v, s;
Node(int v):v(v) { ch[] = ch[] = NULL; s = ; r = rand(); }
int cmp(int x)
{
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 ? : ;
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 == -)
{
if(o->ch[] == NULL) o = o->ch[];
else if(o->ch[] == NULL) o = o->ch[];
else
{
int d2 = o->ch[]->r < o->ch[]->r ? : ;
rotate(o, d2); remove(o->ch[d2], x);
}
}
else remove(o->ch[d], x);
if(o != NULL) o->maintain();
} const int maxc = + ;
struct Command
{
char type;
int x, p;
}cmd[maxc]; const int maxn = + ;
const int maxm = + ; int weight[maxn], from[maxm], to[maxm];
bool removed[maxm];
int n, m, query_cnt;
long long query_tot; int pa[maxn];
int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); } Node* root[maxn]; int kth(Node* o, int k)
{
if(o == NULL || k <= || k > o->s) return ;
int s = o->ch[] == NULL ? : o->ch[]->s;
if(k == s + ) return o->v;
if(k <= s) return kth(o->ch[], k);
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;
} void AddEdge(int x)
{
int u = findset(from[x]);
int 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]); }
}
} void Query(int x, int k)
{
query_cnt++;
query_tot += kth(root[findset(x)], k);
} void ChangeWeight(int x, int v)
{
int u = findset(x);
remove(root[u], weight[x]);
insert(root[u], v);
weight[x] = v;
} int main()
{
//freopen("in.txt", "r", stdin); 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, false, sizeof(removed)); int c = ;
for(;;)
{
char type[]; scanf("%s", type);
if(type[] == 'E') break;
int x, p = , v = ;
scanf("%d", &x);
if(type[] == 'D') removed[x] = true;
if(type[] == 'Q') scanf("%d", &p);
if(type[] == 'C')
{
scanf("%d", &v);
p = weight[x];
weight[x] = v;
}
cmd[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]) AddEdge(i); query_cnt = query_tot = ;
for(int i = c - ; i >= ; i--)
{
char type = cmd[i].type;
int x = cmd[i].x, p = cmd[i].p;
if(type == 'D') AddEdge(x);
if(type == 'Q') Query(x, p);
if(type == 'C') ChangeWeight(x, p);
}
printf("Case %d: %.6f\n", ++kase, query_tot / (double)query_cnt);
} return ;
}

代码君

05-07 15:36