QOJ9431 The Quest for El Dorado 做题记录

QOJ9431 The Quest for El Dorado

Description

给一张 \(n\) 个点,\(m\) 条边的无向图,每条边有长度 \(w\) 和颜色 \(c\)。从节点 \(1\) 开始一共要走 \(k\) 轮,每一轮可以一次性走完颜色均为 \(a_i\) 且长度之和不超过 \(b_i\) 的边或原地不动。问 \(k\) 轮走完以后能走到哪些点。

\(1\leq n,m,k\leq 5\times 10^5\)

Solution

被唐题创飞了。

首先注意到这个问题与最短路模型非常像,所以我们考虑如何设计 “距离”。

我们到达一个点时,首先希望已经经过的轮数尽量小,其次希望在这一轮中走过的距离尽量小,这样有利于后续的扩展。所以我们以 “轮数 \(r\)” 为第一关键字,“本轮走过的距离 \(d\)” 为第二关键字排序。

考虑 Dijkstra 算法。当我们从 \(x\) 扩展到 \(y\) 时,分两种情况讨论:

  1. \((x,y)\) 这条边的颜色与本轮颜色相同,且 \(w(x,y)+d\leq b\)。此时我们直接用 \((r,d+w)\) 尝试扩展;
  2. 否则,我们需要找到 \(a_i=c\)、在 \(r\) 之后、\(b_i\geq w\) 且最靠前的一轮 \(i\),用 \((i,w)\) 尝试扩展。

第二种情况我们可以对每种颜色的轮分别维护一个区间最大值 st 表,查询时二分即可。

时间复杂度 \(O((n+m)\log m+m\log k)\)

启示:最短路模型;设计距离,考虑扩展。

int n,m,k;
int lg[N];

struct Ticket{
	int c,d;
}a[N];

struct TicketSet{
	vector<int> s;
	vector<vector<int> > f;
	int cnt;
	
	void Clear(){
		s.clear();
		f.clear();
		cnt=0;
	}
	
	int Ask(int l,int r){
		int x=lg[r-l+1];
		return max(f[l][x],f[r-(1<<x)+1][x]);
	} 
	
	void Init(){
		cnt=s.size();
		f.resize(cnt+2);
		for(int i=0;i<cnt;i++){
			f[i].resize(lg[cnt]+2);
			f[i][0]=a[s[i]].d;
		}
		for(int j=1;j<=lg[cnt];j++){
			for(int i=0;i+(1<<j)-1<cnt;i++)
				f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); 
		}
	}
	
	int Find(int x,int d){
		int p=lower_bound(s.begin(),s.end(),x)-s.begin();
		int l=p,r=cnt-1,res=-1;
		while(l<=r){
			int mid=(l+r)>>1;
			if(Ask(p,mid)>=d) res=mid,r=mid-1;
			else l=mid+1;
		}
		if(res==-1) return -1;
		else return s[res];
	}
}T[N];

int head[N],tot;

struct Edge{
	int to,nxt,col,val;
}edge[N<<1];

void Add(int u,int v,int c,int w){
	edge[++tot]={v,head[u],c,w};
	head[u]=tot;
}

void Clear(){
	for(int i=1;i<=m;i++) T[i].Clear();
	for(int i=1;i<=n;i++) head[i]=0;
	tot=0;
}

pii dis[N];
bool vis[N];

struct Node{
	int x,r,d;
	
	bool operator<(const Node& tmp)const{
		return r>tmp.r||(r==tmp.r&&d>tmp.d);
	}
};

void Dijkstra(){
	for(int i=1;i<=n;i++){
		vis[i]=0;
		dis[i]={IINF,IINF};
	}
	dis[1]={1,0};
	priority_queue<Node> q;
	q.push({1,1,0});
	while(q.size()){
		int x=q.top().x; q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		int r=dis[x].first,d=dis[x].second;
		for(int i=head[x];i;i=edge[i].nxt){
			int t=edge[i].to;
			if(vis[t]) continue;
			int c=edge[i].col,w=edge[i].val;
			if(a[r].c==c&&d+w<=a[r].d){
				if(dis[t]>pii{r,d+w}){
					dis[t]={r,d+w};
					q.push({t,r,d+w});
				}
			}
			else{
				int p=T[c].Find(r+1,w);
				if(p==-1) continue;
				if(dis[t]>pii{p,w}){
					dis[t]=pii{p,w};
					q.push({t,p,w});
				}
			}
		}
	}
}

void Solve(){
	read(n),read(m),read(k);
	for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
	Clear();
	for(int i=1;i<=m;i++){
		int u,v,c,w;
		read(u),read(v),read(c),read(w);
		Add(u,v,c,w);
		Add(v,u,c,w);
	}
	for(int i=1;i<=k;i++){
		read(a[i].c),read(a[i].d);
		T[a[i].c].s.push_back(i);
	}
	for(int i=1;i<=m;i++) T[i].Init();
	Dijkstra();
	for(int i=1;i<=n;i++){
		if(dis[i].first<=k) putchar('1');
		else putchar('0');
	}
	puts("");
}

signed main(){
	int _; read(_);
	while(_--) Solve();
	return 0;
}
posted @ 2025-05-17 19:46  XP3301_Pipi  阅读(38)  评论(0)    收藏  举报
Title