P9331 [JOIST 2023] 护照 / 线段树优化建图
题目传送门:P9331 [JOIST 2023] 护照。
首先有一个观察,如果从 \(i\) 出发能到达 \([1,i]\) 当且仅当 \(i\) 能走到 \(1\),\([i,n]\) 同理,证明考虑每次前往相当于并起来,到达国家的左右端点只会向边界移动,而 \(1,n\) 正是最左端与最右端。
那么直接建图 \(i \to [l_i,r_i]\),边权为 \(1\),那么每个点的答案就是 \(i\) 到 \(1\) 和 \(n\) 的最短路之和,考虑建反图,在 \(1,n\) 位置跑两次单源最短路,那么每个位置到 \(1,n\) 的距离就随便求了,这里距离就相当于是使用护照个数,注意要特判 \(1,n\)。
但是这样显然算重,由于 \(i\) 到 \(1\) 与 \(i\) 到 \(n\) 若重复肯定是在前面相同,然后分叉永不相交。
枚举分叉的点,那么这个点的权值即为它到 \(1,n\) 的最短路的和,然后真正的答案就是 \(i\) 到各个分叉点的最短路减一加上权值的最小值,这里有可能存在非法情况,但是错解显然不优,跑个多源最短路即可。
使用线段树优化建图优化即可。
#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
const int N=2e5+10;
#define pii pair<int,int>
#define lc k<<1
#define rc k<<1|1
vector<pii>g[N<<2];
int d1[N<<2],vis[N<<2],d2[N<<2],d3[N<<2],id[N];
int n;
inline int read(){
char c=getchar();
int f=1,ans=0;
while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
return ans*f;
}
void build(int k,int l,int r){
if (l==r){id[l]=k;return ;}
int mid=l+r>>1;
build(lc,l,mid),build(rc,mid+1,r);
g[lc].push_back({k,0}),g[rc].push_back({k,0});
}
void change(int k,int l,int r,int l1,int r1,int x){
if (l1<=l&&r1>=r){g[k].push_back({x,1});return ;}
int mid=l+r>>1;
if (l1<=mid) change(lc,l,mid,l1,r1,x);
if (r1>mid) change(rc,mid+1,r,l1,r1,x);
}
inline void dij(int d[],vector<pii>tmp){
for (int i=0;i<N*4;i++) d[i]=1e18,vis[i]=0;
priority_queue<pii,vector<pii>,greater<pii> >q;
for (auto i:tmp) d[i.first]=i.second,q.push({i.second,i.first});
while(!q.empty()){
int u=q.top().second;q.pop();
if (vis[u]) continue;
vis[u]=1;
for (auto i:g[u]){
int v=i.first,w=i.second;
if (d[v]>d[u]+w) d[v]=d[u]+w,q.push({d[v],v});
}
}
}
main(){
n=read();
build(1,1,n);
for (int i=1;i<=n;i++){
int l=read(),r=read();
change(1,1,n,l,r,id[i]);
}
vector<pii>tmp1;tmp1.push_back({id[1],0});dij(d1,tmp1);
vector<pii>tmp2;tmp2.push_back({id[n],0});dij(d2,tmp2);
vector<pii>tmp;
d1[id[1]]=d2[id[n]]=1;
for (int i=1;i<=n;i++) tmp.push_back({id[i],min((int)1e18,d1[id[i]]+d2[id[i]]-1)});
dij(d3,tmp);
int m=read();
while(m--){
int x=read();
printf("%lld\n",d3[id[x]]==(int)1e18?-1:d3[id[x]]);
}
return 0;
}

浙公网安备 33010602011771号