UVALive 5031 Graph and Queries
名次树treap,静态区间第K大,离线算法
刘汝佳白皮234页,讲的很详细,写这博客为了纪念。。这超长的代码。。
传送门:HustOJ
传送门:LA
题意
n(n<=2e4)个顶点m(m<=6e4)条边,每个顶点有个权值val_i, 然后有Q(Q<=5e5)次操作.
操作分为三类:
- D x : 删除第x条边
- Q x k : 查询节点x可达的所有顶点中第k大
- C x V : 将节点x的权值更改为V
输出查询的均值 sum { Query_val } / Query_num
思路
设计一个逆向的离线算法。先读入全部操作,生成最终的图,为每个连通块维护一个名次树。
删边对应加边,若边的两端点位于同一连通块则无所谓,否则合并两个Treap。
修改权值,在操作序列数组里记录修改前的权值,然后修改权值数组中的权值。处理所有操作时在改回来。
查询操作,查询对应Treap中第k大。
关于treap维护第k大:treap本身就是一个二叉搜索树,所以为每个点增加一个值s,表示孩子个数。所以求第k大(当前节点是now):如果k=now右儿子->s+1,说明now就是第k大。如果k小于now右儿子->s+1,说明右面的孩子很多,那么在右孩子里面求第k大。如果大于,说明右面孩子太少了,k大落在左侧。在左儿子中求k-s-1大。
代码
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#define _ ios_base::sync_with_stdio(0),cin.tie(0)
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=20007;
const int oo=0x3f3f3f3f;
typedef long long LL;
const LL loo=4223372036854775807ll;
typedef long double LB;
const LL mod=1e9+7;
//名次树
struct Node
{
Node *ch[2];//左右儿子
int rank;//随机优先级
int val;//值
int s;//孩子总数
Node(int v) { val=v;rank=rand();s=1;ch[0]=ch[1]=NULL; }
int cmp(int x) const
{
if(x==val) return -1;
else return (x<val) ? 0 : 1;
}
void maintain()
{
s=1;
if(ch[0]!=NULL) s+=ch[0]->s;
if(ch[1]!=NULL) s+=ch[1]->s;
}
};
Node* treaproot[MAXN];
void rotate(Node* &o, int d)//d=0代表左旋,d=1代表右旋
{
Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o;
o->maintain();k->maintain();
o=k;
}
int find(Node* o, int x)//在Treapo中查找x
{
while(o!=NULL)
{
int d=o->cmp(x);
if(d==-1) return 1;
else o=o->ch[d];
}
return 0;
}
void insert(Node* &o, int x)//在Treapo中插入key=x的元素
{
if(o==NULL) { o=new Node(x); }
else
{
int d=(x<o->val ? 0 : 1);
insert(o->ch[d], x);
if(o->ch[d]->rank>o->rank) rotate(o, d^1);
}
o->maintain();
}
void remove(Node* &o, int x)//在Treapo中移除key为x的元素
{
int d=o->cmp(x);
if(d==-1)
{
Node* u=o;
if(o->ch[1]==NULL)
{
o=o->ch[0];
delete u;
}
else if(o->ch[0]==NULL)
{
o=o->ch[1];
delete u;
}
else
{
int d2=(o->ch[0]->rank>o->ch[1]->rank ? 1 : 0);
rotate(o, d2);remove(o->ch[d2], x);
}
}
else
{
remove(o->ch[d], x);
}
if(o!=NULL) o->maintain();
}
void release(Node* &o)//释放整颗Treap
{
if(o->ch[0]!=NULL) release(o->ch[0]);
if(o->ch[1]!=NULL) release(o->ch[1]);
delete o;
o=NULL;
}
void merge(Node* &a, Node* &b)
{
if(b->ch[0]!=NULL) merge(a, b->ch[0]);
if(b->ch[1]!=NULL) merge(a, b->ch[1]);
insert(a, b->val);
delete b;b=NULL;
}
int find_Kth(Node* o, int k)
{
if(o==NULL||k<=0||k>o->s) return 0;
else
{
Node* now=o;
while(1)
{
int rson=(now->ch[1]==NULL ? 0 : now->ch[1]->s);
if(k==rson+1) { return now->val; }
else if(k<=rson)
{
now=now->ch[1];
}
else
{
now=now->ch[0];
k=k-rson-1;
}
}
}
}
//名次树
//并查集
int fa[MAXN];
int get_fa(int a)
{
return a==fa[a] ? a : fa[a]=get_fa(fa[a]);
}
//并查集
int op;
int operate[500010][3];//操作指令序列
int querycnt=0;
LL querysum=0;
vector<int> G[MAXN];
int val[MAXN];
struct Edge
{
int from, to, removed;
Edge() {}
Edge(int _a, int _b, int _c) { from=_a;to=_b;removed=_c; }
};
vector<Edge> edge;
void init()//初始化整个
{
for(int i=0;i<MAXN;i++) G[i].clear();
edge.clear();
querycnt=querysum=op=0;
}
void add_edge(int i)
{
int from=edge[i].from, to=edge[i].to;
int fathera=get_fa(from);
int fatherb=get_fa(to);
if(fathera==fatherb) return;
if(treaproot[fathera]->s>treaproot[fatherb]->s)
{
fa[fatherb]=fathera;
merge(treaproot[fathera], treaproot[fatherb]);
}
else
{
fa[fathera]=fatherb;
merge(treaproot[fatherb], treaproot[fathera]);
}
}
void query(int x, int k)
{
int fatherx=get_fa(x);
querycnt++;
querysum+=find_Kth(treaproot[fatherx], k);
}
void change_weight(int a, int b)
{
int fa=get_fa(a);
remove(treaproot[fa], val[a]);
insert(treaproot[fa], b);
val[a]=b;
}
int main()
{
_;
int n;
int kase=0;
while(scanf("%d", &n))
{
init();
int m;scanf("%d", &m);
if(n==0) break;
for(int i=1;i<=n;i++) scanf("%d", &(val[i]));
for(int i=0;i<m;i++)
{
int ta, tb;scanf("%d%d", &ta, &tb);
edge.push_back(Edge(ta, tb, 0));
}
while(1)
{
char type;scanf(" %c", &type);
if(type=='E') break;
if(type=='D')
{
int typed;scanf("%d", &typed);
edge[typed-1].removed=1;
operate[op][0]='D'-'A';
operate[op++][1]=typed;
}
if(type=='Q')
{
int ta, tb;scanf("%d%d", &ta, &tb);
operate[op][0]='Q'-'A';
operate[op][1]=ta;operate[op++][2]=tb;
}
if(type=='C')
{
int ta, tb;scanf("%d%d", &ta, &tb);
operate[op][0]='C'-'A';
operate[op][2]=val[ta];
val[ta]=tb;
operate[op++][1]=ta;
}
}
for(int i=1;i<=n;i++)
{
fa[i]=i;
if(treaproot[i]!=NULL) release(treaproot[i]);
insert(treaproot[i], val[i]);
}
for(int i=0;i<m;i++)
{
if(!edge[i].removed)
add_edge(i);
}
for(int i=op-1;i>=0;i--)
{
if(operate[i][0]+'A'=='Q')
{
query(operate[i][1], operate[i][2]);
}
if(operate[i][0]+'A'=='C')
{
change_weight(operate[i][1], operate[i][2]);
}
if(operate[i][0]+'A'=='D')
{
add_edge(operate[i][1]-1);
}
}
printf("Case %d: %.6lf\n", ++kase, querysum/(double)querycnt);
}
return 0;
}