「Luogu P3273 [SCOI2011]棘手的操作」

题目大意

给出一些点和一些操作,资瓷链接两点,某个点所在的联通块都加上一个数,某个点加上一个数,全部加上一个数,查询单点的值,查询某个点所在的连通块中的最大值,查询所有值中的最大值.

分析

可并堆裸题.因为连边这个东西很难处理,所以考虑离线,考虑对每个点记录一下 \(tim_i\) 表示当前节点连接其他节点的时间.再考虑建树,但是如果直接在需要连接的两点直接连边会出问题,没有办法处理某个时间的连通块,所以考虑用并查集维护一下,这样就可以保证对于某个节点的子节点连接父亲的时间都是在自己连接父亲之前,考虑当前修改的连通块在 DFS 序中必定是连续的一段,且左端点必定是当前节点的某个祖先,那么寻找祖先就可以用倍增来维护,在知道祖先后考虑修改的部分必定包含当前节点的若干子树,且在儿子中链接当前节点的时间是单调递增的,所以可以用二分来快速查询,然后就是一个线段树模板维护区间 \(\max\) 就好了.

代码

#include
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int MAXN=3e5+5;
const int INF=1e9;
int n,m;
vectoredge[MAXN];
char opt[MAXN][2];
int x[MAXN],y[MAXN],v[MAXN];
int father[MAXN][21];
int Find(int now)//并查集部分
{
	if(now==father[now][0])
	{
		return now;
	}
	return father[now][0]=Find(father[now][0]);
}
int tim[MAXN]={MAXN};//记录链接父亲的时间
int val[MAXN];
int id[MAXN];
int dfs[MAXN];
int dfs_cnt=0;
int siz[MAXN];//记录子树大小
void DFS(int now)//遍历所有树
{
	REP(i,1,19)//倍增预处理
	{
		father[now][i]=father[father[now][i-1]][i-1];
	}
	id[now]=++dfs_cnt;//当前节点在dfs序的位置
	dfs[dfs_cnt]=now;
	siz[now]=1;
	if(edge[now].size())
	{
		REP(i,0,edge[now].size()-1)//遍历子节点
		{
			father[edge[now][i]][0]=now;
			DFS(edge[now][i]);
			siz[now]+=siz[edge[now][i]];
		}
	}
}
int l,r;
void Getlr(int now,int now_time)//得到对于某个时间时某个节点的联通快
{
	if(tim[now]>1;
		if(tim[edge[now][middle]]>1)
#define LEFT LSON,left,MIDDLE
#define RIGHT RSON,MIDDLE+1,right
#define NOW now_left,now_right
void PushUp(int now)
{
	sgt[now].sum=sgt[LSON].sum+sgt[RSON].sum;
	sgt[now].max=max(sgt[LSON].max,sgt[RSON].max);
}
void Build(int now=1,int left=1,int right=n)
{
	if(left==right)
	{
		sgt[now].max=sgt[now].sum=val[dfs[left]];
		return;
	}
	Build(LEFT);
	Build(RIGHT);
	PushUp(now);
}
void Down(LazyTag tag,int now,int left,int right)
{
	sgt[now].sum+=1ll*tag.add*(right-left+1);
	sgt[now].max+=tag.add;
	sgt[now].tag.add+=tag.add;
}
void PushDown(int now,int left,int right)
{
	Down(sgt[now].tag,LEFT);
	Down(sgt[now].tag,RIGHT);
	sgt[now].tag.Clean();
}
void Updata(int now_left,int now_right,int add,int now=1,int left=1,int right=n)
{
	if(now_right>opt[i];
		if(opt[i][0]=='U')//对于连边操作连边
		{
			scanf("%d%d",&x[i],&y[i]);
			if(Find(y[i])^Find(x[i]))
			{
				tim[Find(y[i])]=i;
				edge[Find(x[i])].push_back(Find(y[i]));
				father[Find(y[i])][0]=Find(x[i]);
			}
		}
		if(opt[i][0]=='A')
		{
			if(opt[i][1]=='1')
			{
				scanf("%d%d",&x[i],&v[i]);
			}
			if(opt[i][1]=='2')
			{
				scanf("%d%d",&x[i],&v[i]);
			}
			if(opt[i][1]=='3')
			{
				scanf("%d",&v[i]);
			}
		}
		if(opt[i][0]=='F')
		{
			if(opt[i][1]=='1')
			{
				scanf("%d",&x[i]);
			}
			if(opt[i][1]=='2')
			{
				scanf("%d",&x[i]);
			}
			if(opt[i][1]=='3')
			{

			}
		}
	}
	REP(i,1,n)//获得这个森林的dfs序
	{
		if(!tim[i])
		{
			tim[i]=INF;
			DFS(i);
		}
	}
	Build();//建树
	REP(i,1,m)
	{
		if(opt[i][0]=='A')//修改操作
		{
			if(opt[i][1]=='1')
			{
				Updata(id[x[i]],id[x[i]],v[i]);
			}
			if(opt[i][1]=='2')
			{
				Getlr(x[i],i);
				Updata(l,r,v[i]);
			}
			if(opt[i][1]=='3')
			{
				Updata(1,n,v[i]);
			}
		}
		if(opt[i][0]=='F')//查询操作
		{
			if(opt[i][1]=='1')
			{
				printf("%d\n",QueryMax(id[x[i]],id[x[i]]));
			}
			if(opt[i][1]=='2')
			{
				Getlr(x[i],i);
				printf("%d\n",QueryMax(l,r));
			}
			if(opt[i][1]=='3')
			{
				printf("%d\n",QueryMax(1,n));
			}
		}
	}
	return 0;
}

你可能感兴趣的:(「Luogu P3273 [SCOI2011]棘手的操作」)