[SDOI2015]道路修建
不知道为什么之前这篇在博客园上出了一点问题
链接
题解
线段树之什么都能维护
用线段树维护最小生成树。
和[SHOI2008]堵塞的交通很像,一开始想naive了,以为只需要维护上下联通和不联通的答案,两个联通的合并加上中间小的边。
但是这样其实假掉了,一组hack是
7 1
2 5 9 4 7 2
2 10 8 7 7 6
8 2 3 10 7 6 8
Q 1 7
答案应该是 61,而不是 63。
把图画出来后发现其实两个联通的合并的时候中间形成了个环,应该从环上减掉最大的,而不能只看中间连接的两条边。
为了处理这个环,我们需要维护此时一段区间最左/右的竖边及其左/右边的边权最大值。
然后还有一个问题是如果左/右边只有一条竖边并且被减掉了,那么更新的时候就需要用另一边的信息。为了处理这个问题,需要记录最左/右的竖边权值和区间内竖边总数,以及区间内所有横边的最大值。
于是我们就可以处理掉这个pushup了
inline void pushup(node &p,node x,node y){
p.L=x.L,p.R=y.R;int mid=x.R,tt=max(uc[mid],dc[mid]);
p.maxn=max(tt,max(x.maxn,y.maxn));
int t1=max(tt,max(x.maxrr,y.minll)),t=x.con+y.con+uc[mid]+dc[mid]-t1;
p.con=t,p.sum=x.sum+y.sum-1;
p.minl=x.minl,p.minll=x.minll,p.maxr=y.maxr,p.maxrr=y.maxrr;
if(t1!=x.maxr&&t1!=y.minl)p.sum++;
if(t1==x.maxr&&x.sum==1)p.minl=y.minl,p.minll=max(tt,max(y.minll,x.maxn));
else if(t1==y.minl&&y.sum==1)p.maxr=x.maxr,p.maxrr=max(tt,max(x.maxrr,y.maxn));
}
然后修改就和[SHOI2008]堵塞的交通一样,查询答案还要简单一些。
然后就做完了。
code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define cs const
#define in read()
inline int read(){
int p=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-48;c=getchar();}
return p*f;
}
cs int N=60005;
int n,m;
int uc[N],dc[N],c[N];
struct node{
int con,sum,maxr,minl,maxrr,minll,maxn;
int L,R;
}a[N<<2];
inline void pushup(node &p,node x,node y){
p.L=x.L,p.R=y.R;int mid=x.R,tt=max(uc[mid],dc[mid]);
p.maxn=max(tt,max(x.maxn,y.maxn));
int t1=max(tt,max(x.maxrr,y.minll)),t=x.con+y.con+uc[mid]+dc[mid]-t1;
p.con=t,p.sum=x.sum+y.sum-1;
p.minl=x.minl,p.minll=x.minll,p.maxr=y.maxr,p.maxrr=y.maxrr;
if(t1!=x.maxr&&t1!=y.minl)p.sum++;
if(t1==x.maxr&&x.sum==1)p.minl=y.minl,p.minll=max(tt,max(y.minll,x.maxn));
else if(t1==y.minl&&y.sum==1)p.maxr=x.maxr,p.maxrr=max(tt,max(x.maxrr,y.maxn));
}
inline void built(int l,int r,int p){
if(l==r){a[p].sum=1,a[p].maxn=0,a[p].con=a[p].maxr=a[p].minl=a[p].minll=a[p].maxrr=c[l],a[p].L=a[p].R=l;return ;}
int mid=(l+r)>>1;built(l,mid,p<<1),built(mid+1,r,p<<1|1),pushup(a[p],a[p<<1],a[p<<1|1]);
}
inline void change1(int l,int r,int p,int x,int d){
int mid=(l+r)>>1;
if(l==r){a[p].con=a[p].maxr=a[p].minl=a[p].minll=a[p].maxrr=d;return ;}
if(x<=mid)change1(l,mid,p<<1,x,d);
else change1(mid+1,r,p<<1|1,x,d);
pushup(a[p],a[p<<1],a[p<<1|1]);
}
inline void change2(int l,int r,int p,int x,int ud,int d){
int mid=(l+r)>>1;
if(mid==x){
if(ud)uc[x]=d;
else dc[x]=d;
pushup(a[p],a[p<<1],a[p<<1|1]);
return ;
}
if(x<mid)change2(l,mid,p<<1,x,ud,d);
else change2(mid+1,r,p<<1|1,x,ud,d);
pushup(a[p],a[p<<1],a[p<<1|1]);
}
inline node query(int l,int r,int p,int ql,int qr){
if(l>=ql&&r<=qr)return a[p];
int mid=(l+r)>>1;
if(qr<=mid)return query(l,mid,p<<1,ql,qr);
if(ql>mid)return query(mid+1,r,p<<1|1,ql,qr);
node ans;pushup(ans,query(l,mid,p<<1,ql,qr),query(mid+1,r,p<<1|1,ql,qr));
return ans;
}
string s;
int r1,c1,r2,c2,d;
signed main(){
n=in,m=in;
for(int i=1;i<n;i++)uc[i]=in;
for(int i=1;i<n;i++)dc[i]=in;
for(int i=1;i<=n;i++)c[i]=in;
built(1,n,1);
for(int i=1;i<=m;i++){
cin>>s;
if(s[0]=='C'){
r1=in,c1=in,r2=in,c2=in,d=in;
if(c1>c2)swap(c1,c2),swap(r1,r2);
if(r1==r2)change2(1,n,1,c1,r1==1,d);
else change1(1,n,1,c1,d);
}
else{
c1=in,c2=in;
cout<<query(1,n,1,c1,c2).con<<'\n';
}
}
return 0;
}