【bzoj2333】 SCOI2011—棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333 (题目链接)

题意

  N个节点维护一些操作。。

Solution

  我们用可并大根堆进行维护。

  对于每个连通块建一个局部可并堆,因为要询问全局最大值,所以还要对全局建一个全局可并堆记录之前局部可并堆堆顶元素。

  U:合并x所在的堆以及y所在的堆,并在全局堆中删除合并前的局部堆堆顶元素,因为它合并以后已经不是其连通块的堆顶了。

  A1:在堆中删除,更新后再加入堆

  A2:找到其堆顶,对堆顶进行修改并打上标记

  A3:对全局都打个标记即可

  F1:将标记下传后输出

  F2:找到其所在的堆顶输出

  F3:输出全局堆的堆顶

细节

  码农题就是细节多

代码

// bzoj2333
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=300010;
struct heap {int val,tag,l,r,fa;}q[maxn],qq[maxn];
int n,m,rt,Tag;
char op[10];

void pushdown(heap *q,int k) {   //标记下传
	q[q[k].l].val+=q[k].tag;q[q[k].l].tag+=q[k].tag;
	q[q[k].r].val+=q[k].tag;q[q[k].r].tag+=q[k].tag;
	q[k].tag=0;
}
int merge(heap *q,int x,int y) {   //合并
	if (x==0 || y==0) return x+y;
	if (q[x].val<q[y].val) swap(x,y);
	if (q[x].tag) pushdown(q,x);
	q[x].r=merge(q,q[x].r,y);
	q[q[x].r].fa=x;
	swap(q[x].l,q[x].r);
	return x;
}
int find(heap *q,int x) {   //寻找堆顶并下传标记,注意下传标记和向上查询的顺序
	int tmp=x;
	if (q[x].fa) tmp=find(q,q[x].fa);
	if (q[x].tag) pushdown(q,x);
	return tmp;
}
int del(heap *q,int x) {   //删除
	int f=find(q,x);
	int tmp=merge(q,q[x].l,q[x].r);
	if (q[q[x].fa].l==x) q[q[x].fa].l=tmp;
	else q[q[x].fa].r=tmp;
	q[tmp].fa=q[x].fa;
	return f==x ? (tmp ? find(q,tmp) : 0) : f;   //返回删除后该堆的堆顶,此处不是很好处理,最好画个图理解一下
}
int build() {   //对全局建堆
	queue<int> Q;
	for (int i=1;i<=n;i++) Q.push(i),qq[i]=q[i];
	while (Q.size()>1) {
		int x=Q.front();Q.pop();
		int y=Q.front();Q.pop();
		Q.push(merge(qq,x,y));
	}
	return Q.front();
}
void newq(heap *q,int x,int val) {   //新建元素
	q[x].l=q[x].r=q[x].fa=0;
	q[x].val=val;
}
int main() {
	scanf("%d",&n);q[0].val=-inf;
	for (int i=1;i<=n;i++) scanf("%d",&q[i].val);
	rt=build();
	scanf("%d",&m);
	for (int x,y,i=1;i<=m;i++) {
		scanf("%s",op);
		if (op[0]==‘U‘) {
			scanf("%d%d",&x,&y);
			int r1=find(q,x),r2=find(q,y);
			if (r1!=r2) {
				if (merge(q,r1,r2)==r1) rt=del(qq,r2);
				else rt=del(qq,r1);
			}
		}
		if (op[0]==‘A‘) {
			if (op[1]==‘1‘) {
				scanf("%d%d",&x,&y);
				rt=del(qq,find(q,x));
				int k=del(q,x);
				newq(q,x,q[x].val+y);
				k=merge(q,k,x);
				newq(qq,k,q[k].val);
				rt=merge(qq,k,rt);
			}
			if (op[1]==‘2‘) {
				scanf("%d%d",&x,&y);
				int f=find(q,x);
				q[f].val+=y;q[f].tag+=y;
				rt=del(qq,f);
				newq(qq,f,qq[f].val+y);
				rt=merge(qq,rt,f);
			}
			if (op[1]==‘3‘) scanf("%d",&y),Tag+=y;
		}
		if (op[0]==‘F‘) {
			if (op[1]==‘1‘) {
				scanf("%d",&x);
				find(q,x),printf("%d\n",q[x].val+Tag);
			}
			if (op[1]==‘2‘) {
				scanf("%d",&x);
				printf("%d\n",q[find(q,x)].val+Tag);
			}
			if (op[1]==‘3‘) printf("%d\n",qq[rt].val+Tag);
		}
	}
    return 0;
}
时间: 2024-08-26 06:20:48

【bzoj2333】 SCOI2011—棘手的操作的相关文章

bzoj2333[SCOI2011]棘手的操作

bzoj2333[SCOI2011]棘手的操作 题意: 有N个节点,M个操作:连接两个节点.单个节点的权值增加v.节点所在的连通块的所有节点的权值增加v.所有节点的权值增加v.询问节点当前的权值.询问节点所在的连通块中权值最大的节点的权值.询问所有节点中权值最大的节点的权值.N,M≤300000 题解: 可并堆,虽然听说配对堆非常快,但教程太少了不会写,所以去学了斜堆,比较好写.斜堆实际上是一棵二叉树,核心是合并操作,这是一个递归过程,有点像treap的删除操作.斜堆保证复杂度的方法是每次递归合

bzoj2333 [SCOI2011]棘手的操作(洛谷3273)

题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作:U x y: 加一条边,连接第x个节点和第y个节点A1 x v: 将第x个节点的权值增加vA2 x v: 将第x个节点所在的连通块的所有节点的权值都增加vA3 v: 将所有节点的权值都增加vF1 x: 输出第x个节点当前的权值F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值F3: 输出所有节点中,权值最大的节点的权值 输入输出格式 输入格式: 输入的第一行是一个整数

bzoj千题计划217:bzoj2333: [SCOI2011]棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333 读入所有数据,先模拟一遍所有的合并操作 我们不关心联通块长什么样,只关心联通块内有谁 所以可以把一个联通块用一个链表存储 合并x和y时,y的链表整体接到x的链表后面 这样就成了线性结构 按照链表顺序重新给序列标号即可用线段树维护 一遍过,^_^ #include<cstdio> #include<iostream> #include<algorithm> using

bzoj千题计划218:bzoj2333: [SCOI2011]棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333 上次那个是线段树,再发一个左偏树 维护两种左偏树 第一种是对每个联通块维护一个左偏树 第二种是对所有第一种左偏树的根节点维护一个左偏树 #include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define N 300001 /*void read(int &x

bzoj2333[SCOI2011]棘手的操作 洛谷P3273 [SCOI2011]棘手的操作

2333? 先记一下吧,这题现在全部都是照着题解做的,因为怎么改都改不出来,只好对着题解改,以后还要再做过 以后再也不用指针了!太恶心了!空指针可不止直接特判那么简单啊,竟然还要因为空指针写奇怪的分类讨论! 没错,就是那个诡异的55和63行.由于要返回删除x后x所在树的新根,要分类讨论:如果x是根且其两个子节点合并后为空,那么去掉x后新树树根为空:如果x是根且其两个子节点合并后不为空,那么去掉x后新树树根为两个子节点合并后的:如果x不是根,那么去掉x后新树树根为原来的find(x). 另外,打了

2333: [SCOI2011]棘手的操作[离线线段树]

2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2325  Solved: 909[Submit][Status][Discuss] Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权值增加v A2 x v: 将第x个节点所在的连通块的所有

2333: [SCOI2011]棘手的操作[写不出来]

2333: [SCOI2011]棘手的操作 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1979  Solved: 772[Submit][Status][Discuss] Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权值增加v A2 x v: 将第x个节点所在的连通块的所有

【BZOJ2333】棘手的操作(左偏树,STL)

[BZOJ2333]棘手的操作(左偏树,STL) 题面 BZOJ上看把... 题解 正如这题的题号 我只能\(2333\) 神TM棘手的题目... 前面的单点/联通块操作 很显然是一个左偏树+标记 (确实很显然,只是写死人...) 然后对于全局的最大值而言 搞一个\(multi\)来水 看起来真的简单.. 写起来真的想死... 记住:要特判一下已经联通的块就不要再去\(Merge\)了 #include<iostream> #include<cstdio> #include<

【bzoj2333】[SCOI2011]棘手的操作 可并堆+STL-set

题目描述 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边,连接第x个节点和第y个节点 A1 x v: 将第x个节点的权值增加v A2 x v: 将第x个节点所在的连通块的所有节点的权值都增加v A3 v: 将所有节点的权值都增加v F1 x: 输出第x个节点当前的权值 F2 x: 输出第x个节点所在的连通块中,权值最大的节点的权值 F3: 输出所有节点中,权值最大的节点的权值 输入 输入的第一行是一个整数N,代