【BZOJ3995】[SDOI2015]道路修建
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
1 2
2 1
3 1 2
Q 1 3
C 1 2 2 2 3
Q 2 3
Sample Output
7
5
5
HINT
对于全部的数据,1<=N, M<=60000,任何时刻任何一条专用道路的修建费用不超过10^4。
题解:第一次见到这题居然是在计蒜客上。。。一眼看出是线段树区间合并,但是真正写的时候。。。我被要维护的那一大坨东西征服了。于是去看了大爷的做法。
这里还是做一下大爷题解的注释吧:
先梳理出所有要维护的东西:区间端点,区间横边和,竖边个数,左侧(右)侧第一条竖边的值,左(右)侧竖边以及它左(右)侧的所有横边的最大值,MST的权值。
然后加入中间两条边,这样一定会形成环,找到环上最大的边mx。
如果这条边是左(右)侧的唯一一条竖边,则右(左)侧的最左(右)的竖边成为了区间中最左(右)侧的竖边,然后更新最大值。
否侧,直接更新最大值。
#include <cstdio> #include <cstring> #include <iostream> #define lson x<<1 #define rson x<<1|1 using namespace std; const int maxn=60010; int v[maxn][3]; struct node { int l,r,lsm,rsm,lm,rm,hm,sum,cnt; }s[maxn<<2]; int n,m; char str[10]; inline int max(int a,int b,int c) {return max(a,max(b,c));} inline int max(int a,int b,int c,int d) {return max(max(a,b),max(c,d));} inline node merge(node x,node y) { node z; z.l=x.l,z.r=y.r; int mh=max(v[x.r][0],v[x.r][1]),mx=max(x.rm,y.lm,mh); z.sum=x.sum+y.sum+v[x.r][0]+v[x.r][1]-mx,z.hm=max(x.hm,y.hm,mh),z.cnt=x.cnt+y.cnt; if(mx==x.rsm&&x.cnt==1) { z.lsm=y.lsm,z.rsm=y.rsm,z.lm=max(x.hm,y.lm,mh),z.rm=y.rm,z.cnt--; } else if(mx==y.lsm&&y.cnt==1) { z.lsm=x.lsm,z.rsm=x.rsm,z.lm=x.lm,z.rm=max(x.rm,y.hm,mh),z.cnt--; } else { z.lsm=x.lsm,z.rsm=y.rsm,z.lm=x.lm,z.rm=y.rm,z.cnt-=(mx==x.rsm||mx==y.lsm); } return z; } inline void init(node &x) { x.lsm=x.rsm=x.lm=x.rm=x.sum=v[x.l][2],x.hm=0,x.cnt=1; } void build(int l,int r,int x) { if(l==r) { s[x].l=l,s[x].r=r,init(s[x]); return ; } int mid=(l+r)>>1; build(l,mid,lson),build(mid+1,r,rson); s[x]=merge(s[lson],s[rson]); } void updata(int l,int r,int x,int a) { if(l==r) { init(s[x]); return ; } int mid=(l+r)>>1; if(a<=mid) updata(l,mid,lson,a); else updata(mid+1,r,rson,a); s[x]=merge(s[lson],s[rson]); } node query(int l,int r,int x,int a,int b) { if(a<=l&&r<=b) return s[x]; int mid=(l+r)>>1; if(b<=mid) return query(l,mid,lson,a,b); if(a>mid) return query(mid+1,r,rson,a,b); return merge(query(l,mid,lson,a,b),query(mid+1,r,rson,a,b)); } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(); int i,a,b,c,d; for(i=1;i<n;i++) v[i][0]=rd(); for(i=1;i<n;i++) v[i][1]=rd(); for(i=1;i<=n;i++) v[i][2]=rd(); build(1,n,1); for(i=1;i<=m;i++) { scanf("%s",str); if(str[0]=='C') { a=rd(),b=rd(),c=rd(),d=rd(); if(a!=c) v[b][2]=rd(),updata(1,n,1,b); else { if(b>d) swap(b,d); if(a==1) v[b][0]=rd(); else v[b][1]=rd(); updata(1,n,1,b); } } else a=rd(),b=rd(),printf("%d ",query(1,n,1,a,b).sum); } return 0; }//3 3 1 2 2 1 3 1 2 Q 1 3 C 1 2 2 2 3 Q 2 3