• [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
    ",query(1,1,n,L,R).mst);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    汉字转拼音
    多数组求笛卡尔积
    curl post参数,接口接收不到数据问题
    判断IMEI或MEID是否合法
    javascript 可控速度的上下拉菜单
    去掉android点击事件产生的半透明蓝色背景
    go.js是什么
    jQuery效果——动画
    jQuery选择器
    vue全局组件局部组件的使用
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11644351.html
Copyright © 2020-2023  润新知