BZOJ 2333: [SCOI2011]棘手的操作

题目地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2333


题目大意:对于一幅图支持7种操作(详见原题)。


算法讨论:

        通过U x y、F2 x和F3操作,我们发现题目既要求最大值又要支持合并操作,我们可以想到用可并堆进行维护。

        可并堆?如果你愿意的话显然配对堆和Fibonacci堆是很优秀的……但是我用的是斜堆(同学们想想代码复杂度吧……)

        由于既要维护连通块的Max和全局的Max,我们需要开2个堆,一个是维护当前连通块的Max,另一个维护全局的Max。

        在全局的Max中我们只要维护每个连通块堆顶值的Max就可以了,因为每个连通块堆顶的Max一定比该连通块中的其他值更优。

        对于F2操作,我们可以先通过不断地找父亲找到x点的祖先,然后用类似于线段树Lazy的思想进行处理。

        对于A1和A2操作,我们可以先在堆中把相应节点删除,进行相应操作后再添加到堆中。

        在堆的合并操作时,我们可以顺带地把Lazy标记下传。

        在删除节点时,我们需要把其祖先们的Lazy标记累加,因为下一次合并到堆中时一定是标记下传在合并之前进行。相似地,F1操作也需要把其祖先们的Lazy标记累加。

        至于A3这种操作……只需要在全局设个变量记录一下就可以了。

        一题又这么A掉了……


Code:

/*
 * Problem:2333
 * Author:PYC
 */

#include <cstdio>
#include <queue>
#include <algorithm>

#define maxn 300000

using namespace std;

char s[10];
int n,m,root,add;

struct node{
	int key,l,r,fa,add;
}heap1[maxn*2+1],heap2[maxn*2+1];

void down(int x){
	heap1[heap1[x].l].key+=heap1[x].add;
	heap1[heap1[x].l].add+=heap1[x].add;
	heap1[heap1[x].r].key+=heap1[x].add;
	heap1[heap1[x].r].add+=heap1[x].add;
	heap1[x].add=0;
}

int fa(int x){
	int tmp=x;
	while (heap1[tmp].fa) tmp=heap1[tmp].fa;
	return tmp;
}

int sum(int x){
	int tmp=x,sum=0;
	while (tmp=heap1[tmp].fa) sum+=heap1[tmp].add;
	return sum;
}

int merge1(int x,int y){
	if (!x || !y) return x?x:y;
	if (heap1[x].key<heap1[y].key) swap(x,y);
	down(x);
	heap1[x].r=merge1(heap1[x].r,y);
	heap1[heap1[x].r].fa=x;
	swap(heap1[x].l,heap1[x].r);
	return x;
}

int merge2(int x,int y){
	if (!x || !y) return x?x:y;
	if (heap2[x].key<heap2[y].key) swap(x,y);
	heap2[x].r=merge2(heap2[x].r,y);
	heap2[heap2[x].r].fa=x;
	swap(heap2[x].l,heap2[x].r);
	return x;
}

int del1(int x){
	down(x);
	int y=merge1(heap1[x].l,heap1[x].r);
	if (x==heap1[heap1[x].fa].l) heap1[heap1[x].fa].l=y;else heap1[heap1[x].fa].r=y;
	heap1[y].fa=heap1[x].fa;
	return fa(y);
}

void del2(int x){
	int y=merge2(heap2[x].l,heap2[x].r);
	if (root==x) root=y;
	if (x==heap2[heap2[x].fa].l) heap2[heap2[x].fa].l=y;else heap2[heap2[x].fa].r=y;
	heap2[y].fa=heap2[x].fa;
}

void renew1(int x,int v){
	heap1[x].key=v;
	heap1[x].fa=heap1[x].l=heap1[x].r=0;
}

void renew2(int x,int v){
	heap2[x].key=v;
	heap2[x].fa=heap2[x].l=heap2[x].r=0;
}

int heapify(){
	queue<int> Q;
	for (int i=1;i<=n;++i) Q.push(i);
	while (Q.size()>1){
		int x=Q.front();Q.pop();
		int y=Q.front();Q.pop();
		Q.push(merge2(x,y));
	}
	return Q.front();
}

void U(){
	int x,y;scanf("%d%d",&x,&y);
	int fx=fa(x),fy=fa(y);
	if (fx!=fy) if (merge1(fx,fy)==fx) del2(fy);else del2(fx);
}

void A1(){
	int x,v;scanf("%d%d",&x,&v);
	del2(fa(x));
	int y=del1(x);
	renew1(x,heap1[x].key+v+sum(x));
	int z=merge1(y,x);
	renew2(z,heap1[z].key);
	root=merge2(root,z);
}

void A2(){
	int x,v,y;scanf("%d%d",&x,&v);
	del2(y=fa(x));
	heap1[y].key+=v;
	heap1[y].add+=v;
	renew2(y,heap1[y].key);
	root=merge2(root,y);
}

void A3(){
	int v;scanf("%d",&v);
	add+=v;
}

void F1(){
	int x;scanf("%d",&x);
	printf("%d\n",heap1[x].key+sum(x)+add);
}

void F2(){
	int x;scanf("%d",&x);
	printf("%d\n",heap1[fa(x)].key+add);
}

void F3(){
	printf("%d\n",heap2[root].key+add);
}

int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;++i) scanf("%d",&heap1[i].key),heap2[i].key=heap1[i].key;
	root=heapify();
	scanf("%d",&m);
	for (int i=1;i<=m;++i){
		scanf("%s",s);
		if (s[0]=='U') U();
		if (s[0]=='A'){
			if (s[1]=='1') A1();
			if (s[1]=='2') A2();
			if (s[1]=='3') A3();
		}
		if (s[0]=='F'){
			if (s[1]=='1') F1();
			if (s[1]=='2') F2();
			if (s[1]=='3') F3();
		}
	}
	return 0;
}

By Charlie Pan

Mar 9,2014

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值