[bzoj3995] [SDOI2015]道路修建 线段树

时间:2021-12-30 01:55:14

Description

某国有2N个城市,这2N个城市构成了一个2行N列的方格网。现在该国*有一个旅游发展计划,这个计划需要选定L、R两列(L<=R),修建若干条专用道路,使得这两列之间(包括这两列)的所有2(R-L+1)个城市中每个城市可以只通过专用道路就可以到达这2(R-L+1)个城市中的任何一个城市。这种专用道路只能在同一行相邻两列的城市或者同一列的两个城市之间修建,且修建需要花费一定的费用。由于该国*决定尽量缩减开支,因此*决定,选定L、R后,只修建2(R-L+1)-1条专用道路,使得这些专用道路构成一个树结构。现在你需要帮助该国*写一个程序,完成这个任务。具体地,该任务包含M个操作,每个操作的格式如下:

\1. C x0 y0 x1 y1 w:由于重新对第x0行第y0列的城市和第x1行第y1列的城市之间的情况进行了考察,它们之间修建一条专用道路的花费变成了w;

\2. Q L R:若*选定的两列分别为L、R,询问*的最小开支。

Input

第一行,两个整数N、M。

第二行,N-1个整数,其中第i个整数表示初始时第1行第i列的城市和第1行第i+1列的城市之间修建一条专用道路的费用。

第三行,N-1个整数,其中第i个整数表示初始时第2行第i列的城市和第2行第i+1列的城市之间修建一条专用道路的费用。

第四行,N个整数,其中第i个整数表示初始时第1行第i列的城市和第2行第i列的城市之间修建一条专用道路的费用。

接下来的M行,每行一个操作。

Output

对于每个询问操作,输出一行,表示你计算出的*的最小开支。

Sample Input

3 3

1 2

2 1

3 1 2

Q 1 3

C 1 2 2 2 3

Q 2 3

Sample Output

7

5

HINT

对于全部的数据,1<=N, M<=60000,任何时刻任何一条专用道路的修建费用不超过10^4。

Source

Round 1 感谢yts1999上传

Solution

这题就是一个\(2*n\)的网格图,求区间最小生成树,支持单边修改。

我会kruskal!然而并没有什么用

考虑这样的图的最小生成树长什么样,以及是否存在可合并性。

显而易见的是一定至少有一条竖边。

再接着分析,如果要合并两个相邻但不连通的最小生成树,我们有一个非常好的办法:

先连接上下两条边,这时候一定能够形成一个矩形形状的环,我们在这个环中找到最大的元素,并且删去,这样就完成了相邻的最小生成树的合并。

所以我们用线段树的话,每个节点需要维护的信息如下:

左右端点,最左最右竖边的权值,以及最左最右竖边向左(右)延伸到尽头的边最大值(类似一个三面半包围结构),所有横边的最大值,答案,竖边的个数。

合并的时候,左右端点,所有横边的最大值,答案,竖边个数可以直接求出。但是有需要特判的地方:

如果某一边的竖边只有一个,那么新的左右竖边权值,左右竖边左右延伸最大值都要根据情况去变化。

千万要写else!!不然两边权值相等并且两边各只有一个竖边的话会发生奇怪的错误。

其实代码并不难写只是细节有点多

Code

#include <bits/stdc++.h>
using namespace std;
#define N 60005
#define mid ((l+r)>>1)
int a,b,c,d,e,n,m,v[N][3];char o[5];
struct nod{int l,r,lm,rm,ls,rs,am,ans,cnt;void init(int x){lm=rm=ls=rs=ans=v[x][2];cnt=1;am=0;}}s[N*4];
nod merge(nod x,nod y)
{
nod z;z.l=x.l;z.r=y.r;
int mxh=max(v[x.r][0],v[x.r][1]),mx=max(max(x.rm,y.lm),mxh);
z.ans=x.ans+y.ans+v[x.r][0]+v[x.r][1]-mx;z.am=max(max(x.am,y.am),mxh);z.cnt=x.cnt+y.cnt;
if(mx==x.rs&&x.cnt==1) z.lm=max(x.am,max(y.lm,mxh)),z.rm=y.rm,z.ls=y.ls,z.rs=y.rs,z.cnt--;
else if(mx==y.ls&&y.cnt==1) z.lm=x.lm,z.rm=max(y.am,max(mxh,x.rm)),z.ls=x.ls,z.rs=x.rs,z.cnt--;
else z.lm=x.lm,z.rm=y.rm,z.ls=x.ls,z.rs=y.rs,z.cnt-=(mx==x.rs||mx==y.ls);
return z;
}
void bud(int x,int l,int r)
{
if(l==r){s[x].init(l);s[x].l=l;s[x].r=r;return;}
bud(x*2,l,mid);bud(x*2+1,mid+1,r);s[x]=merge(s[x*2],s[x*2+1]);
}
void upd(int x,int l,int r,int p)
{
if(l==r){s[x].init(l);return;}
if(p<=mid) upd(x*2,l,mid,p);else upd(x*2+1,mid+1,r,p);s[x]=merge(s[x*2],s[x*2+1]);
}
nod que(int x,int l,int r,int b,int e)
{
if(b<=l&&r<=e) return s[x];
if(e<=mid) return que(x*2,l,mid,b,e);if(b>mid) return que(x*2+1,mid+1,r,b,e);
return merge(que(x*2,l,mid,b,e),que(x*2+1,mid+1,r,b,e));
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++) scanf("%d",&v[i][0]);
for(int i=1;i<n;i++) scanf("%d",&v[i][1]);
for(int i=1;i<=n;i++) scanf("%d",&v[i][2]);
bud(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%s",o);
if(o[0]=='Q') scanf("%d%d",&a,&b),printf("%d\n",que(1,1,n,a,b).ans);
else
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);if(b>d) swap(b,d);
if(a!=c) v[b][2]=e,upd(1,1,n,b);else v[b][a-1]=e,upd(1,1,n,b);
}
}
}