BZOJ 2333 SCOI2011 棘手的操作 可并堆套可并堆

9 篇文章 0 订阅
5 篇文章 0 订阅

题目大意:给定n个节点,每个节点有一个初始权值,维护以下操作:

1.合并两个联通块

2.某个点权值+x

3.某个点所在联通块权值+x

4.所有点权值+x

5.询问某个点的权值

6.询问某个点所在联通块的最大权值

7.询问所有点之间的最大权值


2333333333333333333333333333333333333333333333333333333333333

2333333333333333333333333333333333333333333333333333333333333

2333333333333333333333333333333333333333333333333333333333333

2333333333333333333333333333333333333333333333333333333333333

2333333333333333333333333333333333333333333333333333333333333


根据题目要求 对于每个联通块,我们需要一种数据结构,可以完成以下操作:

某个点权值+x

整体权值+x

查询某个点权值

查询最大值

合并

显然用可并堆就可以搞- -


那么12356我们就解决了

4只需要维护一个全局标记即可

7需要维护所有点的最大权值,我们很容易想到所有点的最大权值就是所有联通块最大权值的最大值

于是我们需要对所有联通块维护一个数据结构,支持:

插入某个点

删除某个点

修改某个点的权值

查询最大值

这不还是堆么!于是怒写可并堆套可并堆~


细节部分欢迎自行脑补,有益于身体健康~


TMD真是恶心死我了……


2333333333333333333333333333333333333333333333333333333333333333333


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 300300
using namespace std;

struct Heap{//内层随机堆
	Heap *ls,*rs,*fa;
	int val,mark;

	void Add(int x)
	{
		val+=x;
		mark+=x;
	}

	void Push_Down()
	{
		if(!mark) return ;
		if(ls) ls->Add(mark);
		if(rs) rs->Add(mark);
		mark=0;
	}

	void _Push_Down()
	{
		if(fa) fa->_Push_Down();
		Push_Down();
	}

	void Cut()
	{
		_Push_Down();
		if(!fa) return ;
		if(this==fa->ls)
			fa->ls=0x0;
		else
			fa->rs=0x0;
		fa=0x0;
	}

	Heap* Find_Root()
	{
		if(!fa) return this;
		return fa->Find_Root();
	}

	friend Heap* Merge(Heap *x,Heap *y)
	{
		if(!x) return y;
		if(!y) return x;
		if(x->val<y->val)
			swap(x,y);
		y->fa=x;
		x->Push_Down();
		if(rand()&1)
			x->ls=Merge(x->ls,y);
		else
			x->rs=Merge(x->rs,y);
		return x;
	}

}heap2[M];

struct abcd{//外层随机堆
	abcd *ls,*rs,*fa;
	Heap *root;

	void Cut()
	{
		if(!fa) return ;
		if(this==fa->ls)
			fa->ls=0x0;
		else
			fa->rs=0x0;
		fa=0x0;
	}

	friend abcd* Merge(abcd *x,abcd *y)
	{
		if(!x) return y;
		if(!y) return x;
		if(x->root->val<y->root->val)
			swap(x,y);
		y->fa=x;
		if(rand()&1)
			(x->ls=Merge(x->ls,y))->fa=x;
		else
			(x->rs=Merge(x->rs,y))->fa=x;
		return x;
	}

}*root,heap1[M];

abcd *belong[M];//记录每个内层堆堆顶在外层堆的位置

int n,m,mark,a[M];

void Union(int x,int y)//合并x和y所在堆
{
	abcd *fx=belong[heap2[x].Find_Root()-heap2];
	abcd *fy=belong[heap2[y].Find_Root()-heap2];
	if(fx==fy) return ;
	if(fx->root->val<fy->root->val) swap(fx,fy);
	fy->Cut();
	if(fy==root) (root=Merge(root->ls,root->rs))->fa=0x0;
	else (root=Merge(root,Merge(fy->ls,fy->rs)))->fa=0x0;

	fx->root=Merge(fx->root,fy->root);
	belong[fx->root-heap2]=fx;
}

void Add1(int x,int y)//单点修改
{
	abcd *fx=belong[heap2[x].Find_Root()-heap2];
	fx->Cut();heap2[x].Cut();
	
	abcd *temp1=Merge(fx->ls,fx->rs);
	if(temp1) temp1->fa=0x0;
	fx->ls=0x0;fx->rs=0x0;
	
	Heap *temp2=Merge(heap2[x].ls,heap2[x].rs);
	if(temp2) temp2->fa=0x0;
	heap2[x].ls=0x0;heap2[x].rs=0x0;
	
	

	heap2[x].val+=y;

	if(&heap2[x]!=fx->root)
		fx->root=Merge(fx->root,&heap2[x]);
	fx->root=Merge(fx->root,temp2);
	belong[fx->root-heap2]=fx;
	
	if(fx!=root)
		root=Merge(fx,root);
	root=Merge(root,temp1);
}

void Add2(int x,int y) //联通块修改
{
	abcd *fx=belong[heap2[x].Find_Root()-heap2];
	fx->Cut();
	abcd *temp=Merge(fx->ls,fx->rs);
	if(temp) temp->fa=0x0;
	fx->ls=0x0;fx->rs=0x0;

	fx->root->Add(y);

	if(root!=fx) root=Merge(root,fx);
	root=Merge(root,temp);
}

void Add3(int y)//全体修改
{
	mark+=y;
}

int Find1(int x)//单点查询
{
	heap2[x]._Push_Down();
	return heap2[x].val+mark;
}

int Find2(int x)//联通块查询
{
	return heap2[x].Find_Root()->val+mark;
}

int Find3()//整体查询
{
	return root->root->val+mark;
}

int main()
{
	
	freopen("2333.in","r",stdin);
	freopen("2333.out","w",stdout); 
	
	int i,x,y;
	char p[10];

	srand(19980402);

	cin>>n;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		heap1[i].root=&heap2[i];
		heap2[i].val=a[i];
		belong[i]=&heap1[i];
		root=Merge(root,&heap1[i]);
	}

	cin>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%s",p);
		if(p[0]=='U')
			scanf("%d%d",&x,&y),Union(x,y);
		if(p[0]=='A')
		{
			if(p[1]=='1')
				scanf("%d%d",&x,&y),Add1(x,y);
			if(p[1]=='2')
				scanf("%d%d",&x,&y),Add2(x,y);
			if(p[1]=='3')
				scanf("%d",&y),Add3(y);
		}
		if(p[0]=='F')
		{
			if(p[1]=='1')
				scanf("%d",&x),printf("%d\n",Find1(x));
			if(p[1]=='2')
				scanf("%d",&x),printf("%d\n",Find2(x));
			if(p[1]=='3')
				printf("%d\n",Find3());
		}
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值