BZOJ2333: [SCOI2011]棘手的操作

Description

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: 输出所有节点中,权值最大的节点的权值

Input

 

输入的第一行是一个整数N,代表节点个数。

接下来一行输入N个整数,a[1], a[2], …, a[N],代表N个节点的初始权值。

再下一行输入一个整数Q,代表接下来的操作数。

最后输入Q行,每行的格式如题目描述所示。

Output

对于操作F1, F2, F3,输出对应的结果,每个结果占一行。

Sample Input

3

0 0 0

8

A1 3 -20

A1 2 20

U 1 3

A2 1 10

F1 3

F2 3

A3 -10

F3

Sample Output


-10

10

10

HINT



 对于30%的数据,保证 N<=100,Q<=10000


对于80%的数据,保证 N<=100000,Q<=100000


对于100%的数据,保证 N<=300000,Q<=300000


对于所有的数据,保证输入合法,并且 -1000<=v, a[1], a[2], …, a[N]<=1000

Source

如果我们能把点重编号 保证在同一个联通块的编号连续 那么可以用线段树直接维护
考虑把点重编号 用并查集和链表维护
#include<bits/stdc++.h>

using namespace std;

const int maxn=300030;

int n,m;

int nxt[maxn],end[maxn],f[maxn];

int a[maxn],pos[maxn],cnt,b[maxn];

int findfa(int x)
{
	return f[x]==x?x:f[x]=findfa(f[x]);
}

inline int read()
{
	int tmp=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') f=(ch=='-')?-1:f,ch=getchar();
	while(ch>='0'&&ch<='9') tmp=tmp*10+ch-'0',ch=getchar();
	return tmp*f;
}

struct Q
{
	int opt;
	int x,y;
	void in()
	{
		char ch=getchar();
		while(ch<'A'||ch>'Z') ch=getchar();
		switch(ch)
		{
			case 'U':
				opt=0;
				x=read(),y=read();
				break;
			case 'A':
				opt=read(),x=read();
				if(opt<3) y=read();
				break;
			case 'F':
				opt=read()+3;
				if(opt<6) x=read();
				break;
		}
	}
}q[maxn];

struct node
{
	int l,r,mx,flag;
}e[maxn<<2];

void build(int x,int l,int r)
{
	e[x].l=l,e[x].r=r,e[x].flag=0;
	if(l==r)
	{
		e[x].mx=b[l];
		return ;
	}
	int mid=l+r>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	e[x].mx=max(e[x<<1].mx,e[x<<1|1].mx);
}

void pushdown(int x)
{
	e[x<<1].flag+=e[x].flag;
	e[x<<1].mx+=e[x].flag;
	e[x<<1|1].flag+=e[x].flag;
	e[x<<1|1].mx+=e[x].flag;
	e[x].flag=0;
}

void modify(int x,int l,int r,int ql,int qr,int v)
{
	if(l==ql&&r==qr)
	{
		e[x].mx+=v;
		e[x].flag+=v;
		return ;
	}
	pushdown(x);
	int mid=l+r>>1;
	if(qr<=mid)
		modify(x<<1,l,mid,ql,qr,v);
	else if(ql>mid)
		modify(x<<1|1,mid+1,r,ql,qr,v);
	else
		modify(x<<1,l,mid,ql,mid,v),modify(x<<1|1,mid+1,r,mid+1,qr,v);
	e[x].mx=max(e[x<<1].mx,e[x<<1|1].mx);
}

int query(int x,int l,int r,int ql,int qr)
{
//	printf("%d %d %d %d\n",l,r,ql,qr);
	if(l==ql&&r==qr)
		return e[x].mx;
	pushdown(x);
	int mid=l+r>>1;
	if(qr<=mid)
		return query(x<<1,l,mid,ql,qr);
	if(ql>mid)
		return query(x<<1|1,mid+1,r,ql,qr);
	return max(query(x<<1,l,mid,ql,mid),query(x<<1|1,mid+1,r,mid+1,qr));
}

int main()
{
//	freopen("orz.in","r",stdin);
	n=read();
	for(int i=1;i<=n;i++) a[i]=read(),f[i]=end[i]=i;
	m=read();
	for(int i=1;i<=m;i++)
	{
		q[i].in();
		if(!q[i].opt)
		{
			int fx=findfa(q[i].x),fy=findfa(q[i].y);
			if(fx!=fy)
			{
				f[fy]=fx;
				nxt[end[fx]]=fy;
				end[fx]=end[fy];
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(findfa(i)==i)
		{
			for(int j=i;j;j=nxt[j])
				pos[j]=++cnt,b[cnt]=a[j];
		}
	}
	build(1,1,n);
	for(int i=1;i<=n;i++) f[i]=end[i]=i;
	for(int i=1;i<=m;i++)
	{
//		printf("%d %d\n",i,q[i+1].opt);
		int fx,fy;
		switch(q[i].opt)
		{
			case 0:
				fx=findfa(q[i].x),fy=findfa(q[i].y);
				if(fx!=fy)
				{
					f[fy]=fx;
					end[fx]=end[fy];
				}
				break;
			case 1:
				modify(1,1,n,pos[q[i].x],pos[q[i].x],q[i].y);
				break;
			case 2:
				modify(1,1,n,pos[findfa(q[i].x)],pos[end[findfa(q[i].x)]],q[i].y);
				break;
			case 3:
				modify(1,1,n,1,n,q[i].x);
				break;
			case 4:
				printf("%d\n",query(1,1,n,pos[q[i].x],pos[q[i].x]));
				break;
			case 5:
				printf("%d\n",query(1,1,n,pos[findfa(q[i].x)],pos[end[findfa(q[i].x)]]));
				break;
			case 6:
				printf("%d\n",query(1,1,n,1,n));
				break;
		}
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值