题解:P5618 [SDOI2015] 道路修建
题意
给定一个
要求支持以下几种操作:
-
修改某条边的边权。
-
求满足
的点构成的点集的最小生成树。
分析
这道题的想法和 P4246 [SHOI2008] 堵塞的交通 很相似。
注意到
我们考虑如何合并两个区间。
例如,下图的两个区间:
首先,我们将两个区间之间的边
因为
如图:
为了让它成为一棵树,我们需要删掉其中的一条边。
并且为了保证它是一棵最小生成树,我们应该删去边权最大的边。
到这里,我们维护的对象就显而易见了。
我们维护以下值:
lu
:最左侧的垂直的生成树边上端点到左上角这一段中边的边权的最大值(即 段中的边权最大值)ld
:最左侧的垂直的生成树边下端点到左下角这一段中边的边权的最大值(即 段中的边权最大值)ru
:最右侧的垂直的生成树边上端点到右上角这一段中边的边权的最大值(即 段中的边权最大值)rd
:最右侧的垂直的生成树边下端点到右下角这一段中边的边权的最大值(即 段中的边权最大值)lv
:最左侧的垂直的生成树边的边权(即 的边权)rv
:最右侧的垂直的生成树边的边权(即 的边权)ans
:该段的最小生成树的边权和。
我们再回头看我们要合并的东西:
我们要删除的就是
发现如果删除的是
但是如果删去了垂直边,可能出现某一侧区间没有垂直边的情况。
所以我们需要记录一个 cnt
来表示某个区间中有多少条垂直边。
假设左侧失去了垂直边,那么新区间的 lv
就应该是右区间的 lv
,其他的信息同理。
Code
#include<bits/stdc++.h> using namespace std; #define maxn 60004 /* lu ru vu -+-+-+-+-+-+--- ==== ---+-+-+ | | | lv rv | | | | -+-+-+-+-+-+--- ==== ---+-+-+ ld rd vd */ int vu[maxn], vd[maxn]; struct SegT { struct node { int R, lv, rv, lu, ru, ld, rd, cnt, ans; node(int p=0, int v=0) {R=p, lv=rv=ans=v, lu=ru=ld=rd=0, cnt=1;} friend node operator+(node a, node b) { node ret; ret.R=b.R; ret.ans=a.ans+b.ans+vu[a.R]+vd[a.R]; ret.cnt=a.cnt+b.cnt; int mx=max({vu[a.R], vd[a.R], a.ru, a.rd, b.lu, b.ld, a.rv, b.lv}); ret.ans-=mx; ret.lv=a.lv, ret.rv=b.rv; ret.lu=a.lu, ret.ld=a.ld; ret.ru=b.ru, ret.rd=b.rd; if(mx==a.rv) { ret.cnt--; if(a.cnt==1) { ret.lv=b.lv, ret.rv=b.rv; ret.ld=max({a.ld, a.rd, b.ld, vd[a.R]}); ret.lu=max({a.lu, a.ru, b.lu, vu[a.R]}); ret.ru=b.ru, ret.rd=b.rd; } } else if(mx==b.lv) { ret.cnt--; if(b.cnt==1) { ret.lv=a.lv, ret.rv=a.rv; ret.rd=max({b.rd, b.ld, a.rd, vd[a.R]}); ret.ru=max({b.ru, b.lu, a.ru, vu[a.R]}); ret.lu=a.lu, ret.ld=a.ld; } } return ret; } }tr[maxn<<2]; #define lc x<<1 #define rc x<<1|1 #define mid ((l+r)>>1) #define lson lc, l, mid #define rson rc, mid+1, r void modify(int x, int l, int r, int p, int v) { if(l==r) return tr[x]=node(p, v), void(); if(p<=mid) modify(lson, p, v); if(p>mid) modify(rson, p, v); tr[x]=tr[lc]+tr[rc]; } void upd(int x, int l, int r, int p) { if(l==r) return; if(p<=mid) upd(lson, p); if(p>mid) upd(rson, p); tr[x]=tr[lc]+tr[rc]; } auto query(int x, int l, int r, int L, int R) { if(L<=l&&r<=R) return tr[x]; if(R<=mid) return query(lson, L, R); if(L>mid) return query(rson, L, R); return query(lson, L, R)+query(rson, L, R); } }tr; int main() { int n, m; cin>>n>>m; for(int i=1;i<n;i++) cin>>vu[i]; for(int i=1;i<n;i++) cin>>vd[i]; for(int i=1, t;i<=n;i++) { cin>>t; tr.modify(1, 1, n, i, t); } char op; int x0, y0, x1, y1, w, L, R; while(m--) { cin>>op; if(op=='C') { cin>>x0>>y0>>x1>>y1>>w; if(x0==x1) (x0==1?vu:vd)[min(y0, y1)]=w, tr.upd(1, 1, n, min(y0, y1)); else tr.modify(1, 1, n, y0, w); } if(op=='Q') { cin>>L>>R; cout<<tr.query(1, 1, n, L, R).ans<<'\n'; } } }
本文作者:Jimmy-LEEE
本文链接:https://www.cnblogs.com/redacted-area/p/18405330
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步