这道题可以看做是[SHOI2008]堵塞的交通的加强版,由于形同模拟不同人的写法差别很大,强烈建议理解了原理之后自己独立写
题意
给一个(2*m)的网格图,每次操作支持修改一条边的权值,和查询([L,R])(含)的最小生成树
思路
如果做过上面那道题就很容易知道这道题是考(毒瘤的)线段树
只需要维护8个标记(因写法而异)
(mst):当前区间的最小生成树
(l,r):当前区间的左右端点
(lc,rc):最小生成树中,当前区间最左/右边的竖线(显然至少存在一条,否则上下不连通)
(lmx,rmx):最小生成树中,最左/右边的竖线左/右边的所有横线的最大值
(mx):该区间最长的横线
合并两个区间([l,mid],[mid+1,r])时,加上中间两条横边,显然会和两边的竖线形成一个环,从中删掉一条边即可:
-
删掉横边,其它参数直接转移
-
删掉竖边,分情况讨论合并即可(这里会用到(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;
}