题解:P11307 [COTS 2016] 建造费 Pristojba
题解区怎么全部都是
解题思路
考虑优化朴素的最小生成树过程,由区间连边不难想到线段树优化建图,但是仔细思考之后会发现无法构造出合适的连边方式(也有可能是我太弱了),不过感觉线段树优化是一个感觉很正确的方向,于是我们遍历一下常用的最小生成树算法,发现 Prim 算法的流程似乎很容易转化成区间 RMQ 问题,考虑线段树优化 Prim。
考虑 Prim 算法的单轮流程,假设我们已经有了一个加入生成树中的点集
怎么办?我们不妨利用 Prim 连边的对称性,取
整理一下,现在我们需要实现两个数据结构:
维护点集
- 往点集中加入一个点并断开原点集与其的所有连边
- 查询与点集
V 的最短连边
维护点集
- 往点集中删除一个点,连接原点集与其的所有连边
- 查询与点集
U 的最短连边
最短连边使用前文所述的方法维护,往点集中加点的操作可以直接在挂载时与原最优起点取较小值,如何处理删点?不难想到可以直接使用 std::set 暴力维护,直接这样做的复杂度是
是有的,直接在有序序列上在线随机删除的复杂度不太可能低于
剩下就是如何初始化每个节点上的最优起点队列了,如果我们直接把起点下传到连边区间对应节点上进行排序,那么这部分复杂度会退化到最坏
如果还有没解释清楚的地方,看代码吧。
AC 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100005,INF=1e9;
/*
常数起飞了/ll
*/
int n,m;
int p[N];bool vis[N];
struct Node {int p,l,r;};
#define mid (tre[now].l+tre[now].r>>1)
#define lson (now*2)
#define rson (now*2+1)
struct Tre {
int l,r;
int min,min_cost; // 最小 p_i 和最小花费
int tag,mtag,cost_v,min_v; // 标记永久化与最短路径
};
queue<int> que[4*N];
vector<pair<int,int>> vec[N];
struct SGT {
Tre tre[4*N];
void build(int now,int l,int r) {
tre[now].l=l,tre[now].r=r;tre[now].min=tre[now].min_cost=INF;
if(l==r) {tre[now].cost_v=tre[now].min_v=l;return;}
build(lson,l,mid);build(rson,mid+1,r);
}
void pushup(int now) {
if(tre[now].l==tre[now].r) {tre[now].min_cost=p[tre[now].tag]+tre[now].min,tre[now].mtag=tre[now].tag;return;}
if(tre[lson].min<tre[rson].min) tre[now].min=tre[lson].min,tre[now].min_v=tre[lson].min_v;
else tre[now].min=tre[rson].min,tre[now].min_v=tre[rson].min_v;
tre[now].min_cost=min({tre[lson].min_cost,tre[rson].min_cost,p[tre[now].tag]+tre[now].min});
if(tre[now].min_cost==tre[lson].min_cost) tre[now].cost_v=tre[lson].cost_v,tre[now].mtag=tre[lson].mtag;
else if(tre[now].min_cost==tre[rson].min_cost) tre[now].cost_v=tre[rson].cost_v,tre[now].mtag=tre[rson].mtag;
else tre[now].cost_v=tre[now].min_v,tre[now].mtag=tre[now].tag;
}
void change(int now,int p,int k) {
if(tre[now].l>p || tre[now].r<p) return;
if(tre[now].l==tre[now].r) {
tre[now].min=k;
pushup(now);
return;
}
change(lson,p,k);change(rson,p,k);
pushup(now);
}
void cgmin(int now,int l,int r,int k) { // 添加区间
if(tre[now].l>r || tre[now].r<l) return;
if(tre[now].l>=l && tre[now].r<=r) {
if(p[k]<p[tre[now].tag]) tre[now].tag=k;
pushup(now);
return;
}
cgmin(lson,l,r,k);cgmin(rson,l,r,k);
pushup(now);
}
void init_que(int now,vector<Node>& vec) { // 初始化队列
vector<Node> L,R;
for(auto& node:vec) {
if(node.l<=tre[now].l && tre[now].r<=node.r) {
que[now].push(node.p);
continue;
}
if(node.l<=mid) L.push_back(node);
if(node.r>mid) R.push_back(node);
}
if(!que[now].empty()) tre[now].tag=que[now].front();
pushup(now);
if(tre[now].l==tre[now].r) return;
init_que(lson,L);init_que(rson,R);
pushup(now);
}
void del(int now,int l,int r) { // 从队列中懒惰删除
if(tre[now].l>r || tre[now].r<l) return;
if(tre[now].l>=l && tre[now].r<=r) {
while(!que[now].empty() && vis[que[now].front()])
que[now].pop();
if(!que[now].empty()) tre[now].tag=que[now].front();
else tre[now].tag=0;
pushup(now);
return;
}
del(lson,l,r);del(rson,l,r);
pushup(now);
}
};
SGT U,V;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin >> n >> m;
p[0]=INF;
for(int i=1;i<=n;++i) cin >> p[i];
int x,l,r;vector<Node> v;
for(int i=1;i<=m;++i) {
cin >> x >> l >> r;
vec[x].push_back({l,r});
v.push_back({x,l,r});
}
U.build(1,1,n);
V.build(1,1,n);
sort(v.begin(),v.end(),[&](Node a,Node b) {
return p[a.p]<p[b.p];
});
V.init_que(1,v);
for(int i=1;i<=n;++i) U.change(1,i,p[i]);
int nxt=1,ans=0;
for(int i=1;i<=n;++i) {
vis[nxt]=1;
for(auto& [l,r]:vec[nxt]) {
V.del(1,l,r);
U.cgmin(1,l,r,nxt);
}
U.change(1,nxt,INF);
V.change(1,nxt,p[nxt]);
if(i==n) break;
if(U.tre[1].min_cost<=V.tre[1].min_cost)
ans+=U.tre[1].min_cost,nxt=U.tre[1].cost_v;
else
ans+=V.tre[1].min_cost,nxt=V.tre[1].mtag;
}
cout << ans << '\n';
return 0;
}
时空复杂度均为