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;
}
posted @ 2026-01-13 21:17  OTn53_qwq  阅读(21)  评论(0)    收藏  举报