1. 程式人生 > >【bzoj2333】[SCOI2011]棘手的操作 可並堆+STL-set

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

space efi ras 當前 ati blog log down ace

題目描述

N個節點,標號從1N,這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,代表節點個數。

接下來一行輸入N個整數,a[1], a[2], …, a[N]

,代表N個節點的初始權值。

再下一行輸入一個整數Q,代表接下來的操作數。

最後輸入Q行,每行的格式如題目描述所示。

輸出

對於操作F1, F2, F3,輸出對應的結果,每個結果占一行。

樣例輸入

3
0 0 0
8
A1 3 -20
A1 2 20
U 1 3
A2 1 10
F1 3
F2 3
A3 -10
F3

樣例輸出

-10
10
10


題解

可並堆+STL-set

題目中要求維護一個數據結構,支持連通塊合並、單點修改、連通塊修改、單點查詢、連通塊查詢,可並堆無疑是最好的選擇。

對於F3操作,再用一個set維護一下每個連通塊的最大值即可。A3操作的話直接記錄一下總體加了多少,詢問時再加入答案。

這樣嘴上說起來還是挺容易的,然而事實上細節超多。

1.使用set時要時刻註意是否該加東西,是否該刪東西,以及不要“不小心”刪到不存在的元素(不然無故RE死得不知有多慘)

2.可並堆樹高是logn的!這意味著並不需要使用並查集即可完成所有操作,查詢連通塊時直接不斷向上尋找fa即可。

代碼看起來還是挺簡單的

另外親測左偏樹和斜堆差不多,代碼中寫了左偏樹

#include <cstdio>
#include <set>
#define N 300010
using namespace std;
multiset<int> s;
multiset<int>::iterator it;
int w[N] , fa[N] , l[N] , r[N] , d[N] , add[N];
char str[5];
void pushdown(int x)
{
	if(add[x]) w[l[x]] += add[x] , w[r[x]] += add[x] , add[l[x]] += add[x] , add[r[x]] += add[x] , add[x] = 0;
}
void update(int x)
{
	if(fa[x]) update(fa[x]);
	pushdown(x);
}
int find(int x)
{
	return fa[x] ? find(fa[x]) : x;
}
int merge(int x , int y)
{
	if(!x) return y;
	if(!y) return x;
	pushdown(x) , pushdown(y);
	if(w[x] < w[y]) swap(x , y);
	r[x] = merge(r[x] , y) , fa[r[x]] = x;
	if(d[l[x]] < d[r[x]]) swap(l[x] , r[x]);
	d[x] = d[r[x]] + 1;
	return x;
}
int clear(int x)
{
	int t = merge(l[x] , r[x]) , f = fa[x];
	fa[x] = l[x] = r[x] = 0;
	if(x == l[f]) l[f] = t;
	else r[f] = t;
	fa[t] = f;
	return find(t);
}
void del(int x)
{
	s.erase(s.find(x));
}
int main()
{
	int n , i , m , v = 0 , x , y , tx , ty;
	scanf("%d" , &n);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &w[i]) , s.insert(w[i]);
	scanf("%d" , &m);
	d[0] = -1;
	while(m -- )
	{
		scanf("%s" , str);
		if(str[0] == ‘U‘)
		{
			scanf("%d%d" , &x , &y) , tx = find(x) , ty = find(y);
			if(tx != ty)
			{
				if(merge(tx , ty) == tx) del(w[ty]);
				else del(w[tx]);
			}
		}
		else if(str[0] == ‘A‘)
		{
			scanf("%d" , &x);
			if(str[1] == ‘1‘) scanf("%d" , &y) , update(x) , del(w[find(x)]) , w[x] += y , s.insert(w[merge(x , clear(x))]);
			else if(str[1] == ‘2‘) scanf("%d" , &y) , tx = find(x) , del(w[tx]) , w[tx] += y , s.insert(w[tx]) , add[tx] += y;
			else v += x;
		}
		else
		{
			if(str[1] == ‘1‘) scanf("%d" , &x) , update(x) , printf("%d\n" , w[x] + v);
			else if(str[1] == ‘2‘) scanf("%d" , &x) , tx = find(x) , printf("%d\n" , w[tx] + v);
			else printf("%d\n" , *(--s.end()) + v);
		}
	}
	return 0;
}

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