bzoj3995 [SDOI2015]道路修建
2018-02-07 19:06
197 查看
题目链接
分析:
曲神的题解
(曲神表示想要结构体版本的代码,题解最后给出的就是结构体的代码)
bzoj1018的进阶版
一开始看到这道题:好像不是很难,用线段树维护最小生成树
update的时候直接判断上下哪一条横边比较小,连接即可
然而一下就发现了bug:
存在可能性连接点的上下两条边都需要连接
那我们就要深入讨论
代码中的区间划分方式和平成不大一样:
[l.r]−−−>[l.mid]+[mid,r][l.r]−−−>[l.mid]+[mid,r]
之后的讨论都是基于以上条件
这样有一个好处:
每个结点维护的是一个2*2的小图,方便处理线段树结点中的信息(之后会介绍)
当然朴素的线段树那样分割也是可以的
我们在update之前左右区间都是一个完美的联通块
如果出现了上述情况,连接上下的两条边后会出现环
(难道这就是墨菲定律的体现???)
试想如果我们在每一个结点中记录了一个最大值,每次合并的时候删除了,那我们去哪里找次大值作为下次删除的边呢
我们先来看个图:
我们在合并左右区间的时候,如果得到环,那么一定是上面标记出的最小环
所以我们需要删除的边一定在这个最小环上
怎么确定这个环呢?
我们需要记录每个结点中最靠左的竖线和最靠右的竖线:
显然,最左竖线再往左的所有横线一定都被选中
最有竖线再往右的所有横线一定都被选中
在合并区间的时就可以用左区间的→_→竖线和右区间的最左竖线确定这个环
那现在我们就需要在这个环上选一条边删掉:
如果是横边(绿色),直接删掉
如果是竖边,删掉后想一下对于这个大区间“最靠左的竖线和最靠右的竖线”是否发生了改变
lsls:最靠左的竖线
rsrs:最靠右的竖线
lmxlmx:最靠左的竖线再往左的横线中的最小边权(方便查找合并时查找最小环中的最小边权)
rmxrmx:最靠右的竖线再往右的横线中的最小边权(方便查找合并时查找最小环中的最小边权)
sumsum:最小生成树的权值和
维护的时候,当:l+1=rl+1=r,我们就单点插入
修改的时候,不管是横线还是竖线,我们不用纠结到底这个修改在哪里,
因为修改一条边会对整个区间都产生影响,所以直接:
发现是在修改的时候v1,v2数组改错了。。。
询问的时候,如果L=R,应该输出竖线的值
线段树的写法有很多种,有时候可以考虑使每个最小结点维护一个小区间
分析:
曲神的题解
(曲神表示想要结构体版本的代码,题解最后给出的就是结构体的代码)
bzoj1018的进阶版
一开始看到这道题:好像不是很难,用线段树维护最小生成树
update的时候直接判断上下哪一条横边比较小,连接即可
然而一下就发现了bug:
存在可能性连接点的上下两条边都需要连接
那我们就要深入讨论
代码中的区间划分方式和平成不大一样:
[l.r]−−−>[l.mid]+[mid,r][l.r]−−−>[l.mid]+[mid,r]
之后的讨论都是基于以上条件
这样有一个好处:
每个结点维护的是一个2*2的小图,方便处理线段树结点中的信息(之后会介绍)
当然朴素的线段树那样分割也是可以的
我们在update之前左右区间都是一个完美的联通块
如果出现了上述情况,连接上下的两条边后会出现环
那我们就遵循最小生成树的原则:删除环上的最长边
然而这个删除并没有表面上(记录最大值删除)这么简单(难道这就是墨菲定律的体现???)
试想如果我们在每一个结点中记录了一个最大值,每次合并的时候删除了,那我们去哪里找次大值作为下次删除的边呢
我们先来看个图:
我们在合并左右区间的时候,如果得到环,那么一定是上面标记出的最小环
所以我们需要删除的边一定在这个最小环上
怎么确定这个环呢?
我们需要记录每个结点中最靠左的竖线和最靠右的竖线:
显然,最左竖线再往左的所有横线一定都被选中
最有竖线再往右的所有横线一定都被选中
在合并区间的时就可以用左区间的→_→竖线和右区间的最左竖线确定这个环
那现在我们就需要在这个环上选一条边删掉:
如果是横边(绿色),直接删掉
如果是竖边,删掉后想一下对于这个大区间“最靠左的竖线和最靠右的竖线”是否发生了改变
这样线段树的及诶单中就需要维护一下几个值:
mxmx:区间最大边权lsls:最靠左的竖线
rsrs:最靠右的竖线
lmxlmx:最靠左的竖线再往左的横线中的最小边权(方便查找合并时查找最小环中的最小边权)
rmxrmx:最靠右的竖线再往右的横线中的最小边权(方便查找合并时查找最小环中的最小边权)
sumsum:最小生成树的权值和
维护的时候,当:l+1=rl+1=r,我们就单点插入
修改的时候,不管是横线还是竖线,我们不用纠结到底这个修改在哪里,
因为修改一条边会对整个区间都产生影响,所以直接:
change(bh<<1,l,mid,x,min(mid,y)); change(bh<<1|1,mid,r,max(x,mid+1),y);
tip
经历了一个不短的WA过程发现是在修改的时候v1,v2数组改错了。。。
询问的时候,如果L=R,应该输出竖线的值
线段树的写法有很多种,有时候可以考虑使每个最小结点维护一个小区间
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N=100010; struct node{ int ls,rs,mx,lmx,rmx,sum; }; node t[N<<2]; int v1 ,v2 ,v ,n,m; node insert(int l,int r) { node ans; ans.mx=max(v1[l],v2[l]); //横线最大值 if (ans.mx>=max(v[l],v[r])) //竖线 { ans.ls=l; ans.rs=r; //最左最右竖线 ans.lmx=ans.rmx=0; //最左竖线左边(最右竖线右边)的横线最大值 ans.sum=v[l]+v[r]+min(v1[l],v2[l]); } else if (v[l]>v[r]) //选择右边的竖线 { ans.ls=ans.rs=r; ans.lmx=ans.mx; ans.rmx=0; ans.sum=v[r]+v1[l]+v2[l]; } else { ans.ls=ans.rs=l; //选择左边的竖线 ans.lmx=0; ans.rmx=ans.mx; ans.sum=v[l]+v1[l]+v2[l]; } return ans; } node update(node l,node r) { node ans; ans.mx=max(l.mx,r.mx); ans.sum=l.sum+r.sum; ans.ls=l.ls; ans.rs=r.rs; ans.lmx=l.lmx; ans.rmx=r.rmx; int mxx=max(l.rmx,r.lmx); //最小环上的最大横边 if (mxx>=max(v[l.rs],v[r.ls])) //直接删除 ans.sum-=mxx; else if (v[l.rs]>v[r.ls]) //删除左竖线 { ans.sum-=v[l.rs]; if (l.rs==l.ls) //只有一条竖线 ans.ls=r.ls,ans.lmx=max(l.mx,r.lmx); } else { ans.sum-=v[r.ls]; if (r.ls==r.rs) ans.rs=l.rs,ans.rmx=max(r.mx,l.rmx); } return ans; } void build(int bh,int l,int r) { if (l+1==r) { t[bh]=insert(l,r); return; } int mid=(l+r)>>1; build(bh<<1,l,mid); build(bh<<1|1,mid,r); t[bh]=update(t[bh<<1],t[bh<<1|1]); } node ask(int bh,int l,int r,int x,int y) { if (l==x&&r==y) return t[bh]; int mid=(l+r)>>1; if (y<=mid) return ask(bh<<1,l,mid,x,y); else if (x>=mid) return ask(bh<<1|1,mid,r,x,y); else { node A=ask(bh<<1,l,mid,x,mid); node B=ask(bh<<1|1,mid,r,mid,y); return update(A,B); } } void change(int bh,int l,int r,int x,int y) { if (x>y) return; if (l+1==r) { t[bh]=insert(l,r); return; } int mid=(l+r)>>1; change(bh<<1,l,mid,x,min(mid,y)); change(bh<<1|1,mid,r,max(x,mid),y); t[bh]=update(t[bh<<1],t[bh<<1|1]); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<n;i++) scanf("%d",&v1[i]); for (int i=1;i<n;i++) scanf("%d",&v2[i]); for (int i=1;i<=n;i++) scanf("%d",&v[i]); build(1,1,n); char s[5]; int xa,ya,xb,yb,z; for (int i=1;i<=m;i++) { scanf("%s%d%d",s,&xa,&ya); if (s[0]=='C') { scanf("%d%d%d",&xb,&yb,&z); if (xa>xb) swap(xa,xb); if (ya>yb) swap(ya,yb); if (xa==xb) //横线 { if (xa==1) v1[ya]=z; else v2[ya]=z; } else v[ya]=z; //竖线 change(1,1,n,ya,yb); } else { if (xa==ya) printf("%d\n",v[xa]); else printf("%d\n",ask(1,1,n,xa,ya).sum); } } return 0; }
相关文章推荐
- BZOJ 3995 [SDOI2015]道路修建
- [bzoj3995][SDOI2015]道路修建-线段树
- 【BZOJ3995】【SDOI2015】道路修建
- bzoj 3995: [SDOI2015]道路修建 线段树
- BZOJ 3995 Sdoi2015 道路修建 线段树
- 【BZOJ3995】[SDOI2015]道路修建 线段树区间合并
- 3995: [SDOI2015]道路修建
- SDOI2015 道路修建
- 【线段树】bzoj3995 [SDOI2015]道路修建
- BZOJ 3995: [SDOI2015]道路修建
- BZOJ 3995 SDOI2015 道路修建
- bzoj3995[SDOI2015]道路修建
- [BZOJ 3995] [SDOI2015] 道路修建 【线段树维护连通性】
- BZOJ 3990: [SDOI2015]排序
- bzoj 2435: [Noi2011]道路修建 树上 dp
- 图论 洛谷P2052 道路修建
- NOI2011道路修建
- [BZOJ4086][Sdoi2015]travel && 暴力
- BZOJ3991: [SDOI2015]寻宝游戏
- BZOJ 4085:[Sdoi2015]bigyration(SDOI 2015 round 2 Day 1)