动态二插排序树的一种常用实现算法:Treap。
从树根向下递归时当然都是一般的BST方式,左小又大,一步步向下;而向上回溯时却是采用维护最大堆的方式,同时为了维护二叉排序树的基本特性,每次不是直接交换节点,而是旋转;
从基本的Treap中为了解题,衍生出了名次树,每个节点用一个数据记录该子树所有节点数,可以完成查询第K大数(第K小数),找任给的x在当前树中的名次;
注意maintain的实现方式和位置,位置很重要,基本原则就是每次更新了节点(添加,删除,旋转),最后都要维护一下;
本题其实还要复杂的多,用到了启发式合并(大概就是从后往前,逆序的意思?),然后同时维护多个Treap树,除了基本的insert,remove,还要完成合并两个树mergoto的操作,自然要用到基本的并查集,哎,白书上说让大家独立手拍;
这题综合性相当强,时间复杂度是n*logn*logn。
#include <cstdlib>
struct node {
node *ch[2];
int r;
int v;
int s;
node(int v):v(v) { ch[0]=ch[1]=NULL; r=rand(); s=1; }
bool operator < (const node& rhs) const {
return r < rhs.r;
}
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;
}
};
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] > o) 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]>o->ch[1]? 1 : 0);
rotate(o,d2);
remove(o->ch[d2],x);
}else {
if (o->ch[0] ==NULL) o=o->ch[1];
else o=o->ch[0];
delete u;
}
}else
remove(o->ch[d],x);
if (o!=NULL) o->maintain();
}
#include <cstdio>
#include <cstring>
#include <vector>
//#include <fstream>
using namespace std;
const int maxc= 500000+10;
struct Commond {
char type;
int x, p;
}commonds[maxc];
const int maxn=20000+10;
const int maxm=60000+10;
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];
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;
else if (k<=s) return kth(o->ch[1],k);
else return kth(o->ch[0],k-s-1);
}
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;
}
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(){
// freopen("1.in","r",stdin);
int kase=0;
while (scanf("%d%d",&n, &m)==2 && n) {
for (int i=1;i<=n;i++) scanf("%d",&weight[i]);
for (int i=1;i<=m;i++) scanf("%d%d",&from[i],&to[i]);
memset(removed,0,sizeof(removed));
int c=0;
for (;;) {
char type;
int x, p=0, v=0;
scanf(" %c",&type);
if (type=='E') break;
scanf("%d",&x);
if (type=='D') removed[x]=1;
if (type=='Q') scanf("%d",&p);
if (type=='C') {
scanf("%d",&v);
p=weight[x];
weight[x]= v;
}
commonds[c++]= (Commond) {type,x,p};
}
for (int i=1;i<=n;i++) {
pa[i]=i; if (root[i]!=NULL) removetree(root[i]);
root[i]= new node(weight[i]);
}
for (int i=1;i<=m;i++)
if (!removed[i]) add_edge(i);
query_tot= query_cnt = 0;
for (int i=c-1;i>=0;i--) {
if (commonds[i].type=='D') add_edge(commonds[i].x);
if (commonds[i].type=='Q') query(commonds[i].x,commonds[i].p);
if (commonds[i].type=='C') change_weight(commonds[i].x,commonds[i].p);
}
printf("Case %d: %.6lf\n",++kase,query_tot/(double)query_cnt);
}
return 0;
}