hdu3726:Graph and Queries(treap+启发式合并+离线)

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3726

题目大意:给出一幅无向边构成的图,每个点有一个点权。有3种操作:删掉一条边,更改一个点的点权,以及查询一个点所在的连通块的第k大点权。

分析:这里我们可以通过对每一个连通块维护一棵treap在log(n)的时间内完成操作2,3,至于操作1,我们发现分离比较难办,于是我们考虑离线读入,然后从后往前做,逐个合并treap。这里如果采用启发式合并,时间复杂度O(n*log^2(n))。

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
#include<ctime>
using namespace std;

const int maxn=40010;
const int maxm=120010;
const int maxw=800100;
const int lg=22;

struct Tnode
{
	int val,fix,Size;
	Tnode *lson,*rson;
	int Lsize() { return lson?lson->Size:0; }
	int Rsize() { return rson?rson->Size:0; }
	void Get_size() { Size=Lsize()+Rsize()+1; }
} tree[maxn*lg];
Tnode *Root[maxn];
int fa[maxn];
int cur;

int u[maxm];
int v[maxm];
int V[maxn];
bool use[maxm];

struct data
{
	int id,a,b;
} work[maxw];

char op[10];
int n,m,w,numq;
long long sum;
int Case=0;

Tnode *New_node(int nv)
{
	cur++;
	tree[cur].val=nv;
	tree[cur].fix=rand();
	tree[cur].Size=1;
	tree[cur].lson=tree[cur].rson=NULL;
	return tree+cur;
}

void Right_turn(Tnode *&P)
{
	Tnode *W=P->lson;
	P->lson=W->rson;
	W->rson=P;
	P=W;
	P->rson->Get_size();
	P->Get_size();
}

void Left_turn(Tnode *&P)
{
	Tnode *W=P->rson;
	P->rson=W->lson;
	W->lson=P;
	P=W;
	P->lson->Get_size();
	P->Get_size();
}

void Insert(Tnode *&P,int nv)
{
	if (!P) P=New_node(nv);
	else
	{
		if ( nv<P->val )
		{
			Insert(P->lson,nv);
			if ( P->lson->fix < P->fix ) Right_turn(P);
			else P->Get_size();
		}
		else
		{
			Insert(P->rson,nv);
			if ( P->rson->fix < P->fix ) Left_turn(P);
			else P->Get_size();
		}
	}
}

void Up(int x)
{
	if (x==fa[x]) return;
	Up(fa[x]);
	fa[x]=fa[ fa[x] ];
}

void Copy(Tnode *&root,Tnode *P)
{
	if (!P) return;
	Insert(root,P->val);
	Copy(root,P->lson);
	Copy(root,P->rson);
}

void Add(int x,int y)
{
	Up(x);
	Up(y);
	x=fa[x];
	y=fa[y];
	if (x==y) return;
	if ( Root[x]->Size < Root[y]->Size )
	{
		Copy(Root[y],Root[x]);
		fa[x]=y;
	}
	else
	{
		Copy(Root[x],Root[y]);
		fa[y]=x;
	}
}

void Delete(Tnode *&P,int nv)
{
	if ( nv==P->val )
	{
		if ( P->lson==NULL )
			if ( P->rson==NULL ) P=NULL;
			else P=P->rson;
		else
			if ( P->rson==NULL ) P=P->lson;
			else
			{
				if ( P->lson->fix < P->rson->fix )
				{
					Right_turn(P);
					Delete(P->rson,nv);
				}
				else
				{
					Left_turn(P);
					Delete(P->lson,nv);
				}
				P->Get_size();
			}
	}
	else
	{
		if ( nv<P->val ) Delete(P->lson,nv);
		else Delete(P->rson,nv);
		P->Get_size();
	}
}

int Ask(Tnode *P,int rank)
{
	int ls=P->Lsize()+1;
	if (rank<ls) return Ask(P->lson,rank);
	if (rank>ls) return Ask(P->rson,rank-ls);
	return P->val;
}

int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	
	srand( time(0) );
	rand(); rand();
	
	scanf("%d%d",&n,&m);
	while ( n || m )
	{
		Case++;
		cur=-1;
		for (int i=1; i<=n; i++)
		{
			Root[i]=NULL;
			scanf("%d",&V[i]);
			fa[i]=i;
		}
		
		for (int i=1; i<=m; i++)
		{
			use[i]=true;
			scanf("%d%d",&u[i],&v[i]);
		}
		
		numq=w=0;
		sum=0;
		scanf("%s",&op);
		while (op[0]!='E')
		{
			w++;
			if (op[0]=='D')
			{
				work[w].id=0;
				scanf("%d",&work[w].a);
				use[ work[w].a ]=false;
			}
			if (op[0]=='C')
			{
				work[w].id=1;
				scanf("%d",&work[w].a);
				work[w].b=V[ work[w].a ];
				scanf("%d",&V[ work[w].a ]);
			}
			if (op[0]=='Q')
			{
				work[w].id=2;
				scanf("%d%d",&work[w].a,&work[w].b);
				numq++;
			}
			scanf("%s",&op);
		}
		
		for (int i=1; i<=n; i++) Insert(Root[i],V[i]);
		for (int i=1; i<=m; i++)
			if (use[i]) Add(u[i],v[i]);
			
		for (int i=w; i>=1; i--)
		{
			if (!work[i].id) Add(u[ work[i].a ],v[ work[i].a ]);
			if (work[i].id==1)
			{
				int wa=work[i].a;
				Up(wa);
				Delete(Root[ fa[wa] ],V[wa]);
				Insert(Root[ fa[wa] ],V[wa]=work[i].b);
			}
			if (work[i].id==2)
			{
				int wa=work[i].a;
				Up(wa);
				int k=Root[ fa[wa] ]->Size-work[i].b+1;
				if ( k<1 || k>Root[ fa[wa] ]->Size ) continue;
				sum+=(long long)Ask(Root[ fa[wa] ],k);
			}
		}
		
		double ans=(double)sum/(double)numq;
		printf("Case %d: %.6lf\n",Case,ans);
		scanf("%d%d",&n,&m);
	}
	
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值