专栏文章
题解:P13823 「Diligent-OI R2 C」所谓伊人
P13823题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @mio63hek
- 此快照首次捕获于
- 2025/12/02 13:58 3 个月前
- 此快照最后确认于
- 2025/12/02 13:58 3 个月前
这有点色情了吧。
一个分层图最短路的解法,个人认为挺好想的。
本文连通块指将有向边视为无向边后的连通块。
首先,因为每次图都是原来的图,那么很显然,每个点能达到的最大值绝对是该连通块的最大值。
你会发现,因为这个交换是有传递性的,所以假如你想从 且最短路上经过了 ,那么不存在一个 满足将有向边看作无向边时, 可抵达 。
然后我们就钦定连通块内只有一个最大值,那么从最大值经过的点来考虑,假设路上经过了 ,绝对是有假如 能顺着边到达 , 也能顺着边到达 ,且 顺着边到达 。
考虑假如 不能顺着边到达,那么将图中所有边翻转方向后, 能顺着边到达 。
然后你会发现一个更惊人的事实,假如 要交换到 ,可以视作经过若干次交换一条边两边的元素且不耗次数,最后将次数 。
那么也就是说我们建个将边翻转的图,然后你假如在一个点,你去另外一个图的代价为 (即终止这次交换),这样可以视作 和另外一张图的 连一条边权为 的无向边,且两张图上的边权都为 。。
那么假如我们知道了一个连通块的所有最大值位置,那么思路就有了,建出刚刚说的图,然后以所有最大值位置为起点,跑一遍多源最短路(当然边权为 01 所以也可以跑一遍 01 bfs)。
找到一个连通块的所有最大值位置可以直接并查集即可。
时间复杂度 ,瓶颈在于 dij,当然可以 01 bfs 做到 ,此时瓶颈在并查集。但是还可以直接搜出所有连通块做到 。反正我常数较小可以 过。
CPP#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
const int N=1e6+5;
int n,m,p[N];
vector<pii> e[N];
int d[N],mx[N];
int find(int x){
if(x==d[x]) return x;
else return d[x]=find(d[x]);
}
int dis[N],vis[N];
void dij(){
priority_queue<pii,vector<pii>,greater<pii>> qp;
fill(dis+1,dis+2*n+1,1e9);
for(int i=1;i<=n;i++) if(p[i]==mx[find(i)]){
qp.push({0,i});
qp.push({0,n+i});
dis[i]=dis[n+i]=0;
}
while(!qp.empty()){
int f=qp.top().second;qp.pop();
if(vis[f]) continue;
vis[f]=1;
for(pii i:e[f]){
if(dis[i.first]>dis[f]+i.second){
dis[i.first]=dis[f]+i.second;
qp.push({dis[i.first],i.first});
}
}
}
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>p[i];
for(int i=1;i<=n;i++) d[i]=i;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
e[u].push_back({v,0});
e[v+n].push_back({u+n,0});
d[find(u)]=find(v);
}
for(int i=1;i<=n;i++) e[i].push_back({i+n,1}),e[i+n].push_back({i,1});
for(int i=1;i<=n;i++) mx[find(i)]=max(mx[find(i)],p[i]);
dij();
for(int i=1;i<=n;i++) {
if(p[i]==mx[find(i)]) cout<<0<<' ';
else cout<<1+min(dis[i],dis[n+i])<<' ';
}
return 0;
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...