[SDOI2015]道路修建(线段树维护连通性)

这道题可以看做是[SHOI2008]堵塞的交通的加强版,由于形同模拟不同人的写法差别很大,强烈建议理解了原理之后自己独立写

题意

给一个\(2*m\)的网格图,每次操作支持修改一条边的权值,和查询\([L,R]\)(含)的最小生成树

思路

如果做过上面那道题就很容易知道这道题是考(毒瘤的)线段树

需要维护8个标记(因写法而异)

\(mst\):当前区间的最小生成树

\(l,r\):当前区间的左右端点

\(lc,rc\):最小生成树中,当前区间最左/右边的竖线(显然至少存在一条,否则上下不连通)

\(lmx,rmx\):最小生成树中,最左/右边的竖线左/右边的所有横线的最大值

\(mx\):该区间最长的横线

合并两个区间\([l,mid],[mid+1,r]\)时,加上中间两条横边,显然会和两边的竖线形成一个环,从中删掉一条边即可:

  1. 删掉横边,其它参数直接转移

  2. 删掉竖边,分情况讨论合并即可(这里会用到\(mx\)

P.S. 不用担心记录的\(lmx,rmx,mx\)边被删掉的情况,证明很简单,可以使用反证法,请独立证明

Code(主要看pushup部分)

#include<bits/stdc++.h>
#define N 60005
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
using namespace std;
int n,m,R[2][N],C[N];
struct Node
{
	Node() {mst=lmx=rmx=0;}
	int l,r,mst,lc,rc,lmx,rmx,mx;
}tr[N<<2];

template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
Node pushup(Node l,Node r)
{
	Node ret;
	ret.mx=Max(Max(l.mx,r.mx),Max(R[0][l.r],R[1][l.r]));
	ret.l=l.l; ret.r=r.r;
	ret.mst=l.mst+r.mst+R[0][l.r]+R[1][l.r];
	
	int mx=Max(Max(R[0][l.r],R[1][l.r]),Max(l.rmx,r.lmx));
	if(C[l.rc]<mx&&C[r.lc]<mx)//断一条横边 
	{
		ret.lc=l.lc; ret.lmx=l.lmx;
		ret.rc=r.rc; ret.rmx=r.rmx;
		ret.mst-=mx;
	}
	else//断一条竖边 
	{
		ret.mst-=Max(C[l.rc],C[r.lc]);
		if(C[l.rc]<C[r.lc])//断右边 
		{
			if(r.lc==r.rc)//右边只有一条竖边 
			{
				ret.rc=l.rc;
				ret.rmx=Max(Max(l.rmx,r.mx),Max(R[0][l.r],R[1][l.r]));
			}
			else
			{
				ret.rc=r.rc;
				ret.rmx=r.rmx;
			}
			ret.lc=l.lc;
			ret.lmx=l.lmx;
		}
		else//断左边 
		{
			if(l.lc==l.rc)
			{
				ret.lc=r.lc;
				ret.lmx=Max(Max(l.mx,r.lmx),Max(R[0][l.r],R[1][l.r]));
			}
			else
			{
				ret.lc=l.lc;
				ret.lmx=l.lmx;
			}
			ret.rc=r.rc;
			ret.rmx=r.rmx;
		}
	}
	return ret;
}
void build(int rt,int l,int r)
{
	if(l==r)
	{
		tr[rt].mst=C[l];
		tr[rt].l=tr[rt].r=l;
		tr[rt].lc=tr[rt].rc=l;
		tr[rt].mx=tr[rt].lmx=tr[rt].rmx=0;
		return;
	}
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	tr[rt]=pushup(tr[rt<<1],tr[rt<<1|1]);
}
void update(int rt,int l,int r,int x)
{
	if(l==r) { tr[rt].mst=C[l]; return; }
	int mid=(l+r)>>1;
	if(x<=mid) update(rt<<1,l,mid,x);
	else update(rt<<1|1,mid+1,r,x);
	tr[rt]=pushup(tr[rt<<1],tr[rt<<1|1]);
}
Node query(int rt,int l,int r,int x,int y)
{
	if(x<=l&&r<=y) return tr[rt];
	int mid=(l+r)>>1;
	if(y<=mid) return query(rt<<1,l,mid,x,y);
	if(x>mid) return query(rt<<1|1,mid+1,r,x,y);
	return pushup(query(rt<<1,l,mid,x,y),query(rt<<1|1,mid+1,r,x,y));
}
int main()
{
	read(n);read(m);
	for(int i=1;i<n;++i) read(R[0][i]);
	for(int i=1;i<n;++i) read(R[1][i]);
	for(int i=1;i<=n;++i) read(C[i]);
	build(1,1,n);
	while(m--)
	{
		char op[2];
		scanf("%s",op);
		if(op[0]=='C')
		{
			int x[2],y[2],w;
			read(x[0]);read(y[0]);
			read(x[1]);read(y[1]);
			read(w);
			if(x[0]==x[1])
			{
				R[x[0]-1][Min(y[0],y[1])]=w;
				update(1,1,n,y[0]);
				update(1,1,n,y[1]);
			}
			else
			{
				C[y[0]]=w;
				update(1,1,n,y[0]);
			}
		}
		else
		{
			int L,R;
			read(L);read(R);
			printf("%d\n",query(1,1,n,L,R).mst);
		}
	}
	return 0;
}
posted @ 2019-10-09 21:01  擅长平地摔的艾拉酱  阅读(208)  评论(0编辑  收藏  举报
/*取消选中*/