P3273-[SCOI2011]棘手的操作【线段树,并查集】

正题

题目链接:https://www.luogu.com.cn/problem/P3273


题目大意

\(n\)个点有权值,要求支持操作

  1. 连接两个点
  2. 单点加权
  3. 联通块加权
  4. 全图加权
  5. 单点询问
  6. 联通块询问最大值
  7. 全图询问最大值

解题思路

把所有可能产生的联通块都变到一个区间里就好了

考虑怎么排这个东西,可以先离线,每次加边的时候连接两个联通块根,这样跑出来的\(dfs\)序就是符合要求的。

注意因为邻接表是按照加边的顺序倒着枚举的,所以要反过来加边。

然后用线段树直接维护答案就好了

时间复杂度\(O(n\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3e5+10;
struct qnode{
	char op[3];
	int x,y;
}p[N];
struct node{
	int to,next;
}a[N];
int n,q,tot,cnt,ls[N],w[N],fa[N],l[N],r[N];
struct SegTree{
	int w[N<<2],lazy[N<<2];
	void Downdata(int x){
		if(!lazy[x])return;
		w[x*2]+=lazy[x];lazy[x*2]+=lazy[x];
		w[x*2+1]+=lazy[x];lazy[x*2+1]+=lazy[x];
		lazy[x]=0;return;
	}
	void Change(int x,int L,int R,int l,int r,int val){
		if(L==l&&R==r){w[x]+=val;lazy[x]+=val;return;}
		int mid=(L+R)>>1;Downdata(x);
		if(r<=mid)Change(x*2,L,mid,l,r,val);
		else if(l>mid)Change(x*2+1,mid+1,R,l,r,val);
		else Change(x*2,L,mid,l,mid,val),Change(x*2+1,mid+1,R,mid+1,r,val);
		w[x]=max(w[x*2],w[x*2+1]);return;
	}
	int Ask(int x,int L,int R,int l,int r){
		if(L==l&&R==r){return w[x];}
		int mid=(L+R)>>1;Downdata(x);
		if(r<=mid)return Ask(x*2,L,mid,l,r);
		if(l>mid)return Ask(x*2+1,mid+1,R,l,r);
		return max(Ask(x*2,L,mid,l,mid),Ask(x*2+1,mid+1,R,mid+1,r));
	}
}T;
void addl(int x,int y){
	if(x==y)return;
	a[++tot].to=y;
	a[tot].next=ls[x];
	ls[x]=tot;return;
}
int find(int x)
{return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
void dfs(int x){
	l[x]=r[x]=++cnt;T.Change(1,1,n,cnt,cnt,w[x]);
	for(int i=ls[x];i;i=a[i].next)
		dfs(a[i].to);
	return;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&w[i]),fa[i]=i;
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		scanf("%s",p[i].op);
		if(p[i].op[0]=='U'){
			scanf("%d%d",&p[i].x,&p[i].y);
			p[i].x=find(p[i].x);p[i].y=find(p[i].y);
			if(p[i].x==p[i].y)continue;
			if(p[i].x<p[i].y)swap(p[i].x,p[i].y);
			fa[p[i].y]=p[i].x;
		}
		else if(p[i].op[0]=='A'&&p[i].op[1]=='1')
			scanf("%d%d",&p[i].x,&p[i].y);
		else if(p[i].op[0]=='A'&&p[i].op[1]=='2')
			scanf("%d%d",&p[i].x,&p[i].y);
		else if(p[i].op[0]!='F'||p[i].op[1]!='3')
			scanf("%d",&p[i].x);
	}
	for(int i=q;i>=1;i--)
		if(p[i].op[0]=='U')addl(p[i].x,p[i].y);
	for(int i=1;i<=n;i++)
		if(find(i)==i)dfs(i);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=q;i++){
		int x=p[i].x,y=p[i].y;
		if(p[i].op[0]=='U'){
			if(x==y)continue;
			fa[y]=x;r[x]=r[y];
		}
		else if(p[i].op[0]=='A'&&p[i].op[1]=='1')
			T.Change(1,1,n,l[x],l[x],y);
		else if(p[i].op[0]=='A'&&p[i].op[1]=='2')
			x=find(x),T.Change(1,1,n,l[x],r[x],y);
		else if(p[i].op[0]=='A'&&p[i].op[1]=='3')
			T.Change(1,1,n,1,n,x);
		else if(p[i].op[0]=='F'&&p[i].op[1]=='1')
			printf("%d\n",T.Ask(1,1,n,l[x],l[x]));
		else if(p[i].op[0]=='F'&&p[i].op[1]=='2')
			x=find(x),printf("%d\n",T.Ask(1,1,n,l[x],r[x]));
		else printf("%d\n",T.Ask(1,1,n,1,n));
	}
	return 0;
}
posted @ 2021-02-07 18:25  QuantAsk  阅读(53)  评论(0编辑  收藏  举报