把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

CF1163F Indecisive Taxi Fee 分析

题目概述

有一个含 \(n\) 个点,\(m\) 条边的无向图,然后有 \(q\) 次询问,每次询问将某一条边的权值改变,问你 \(1\rightarrow n\) 的最短路长度是多少。

\(1\leq n,m,q\leq 2\cdot 10^5\)

分析

假设一条 \(1\rightarrow n\) 的路径 \(P\),长度为 \(D\)

从询问考虑(其实就是只考虑一次询问时)分为下面几种情况:

  • 如果修改的边在 \(P\) 上面,且权值变小,那么最短路径不变。
  • 如果修改的边不在 \(P\) 上面,且权值变大,不用理会。
  • 如果修改的边不在 \(P\) 上面,且权值变小,则强制经过这条边,观察是否会变小。
  • 如果修改的边在 \(P\) 上面,且权值变大,那么这个将会是我们一大难点。

首先我们应当大胆猜测:最短路径只会变成两种情况:

  • 不变。
  • 包含这一条边的一个部分不经过。

显然成立。

第二部分怎么处理呢?

我们对于每一条边,假设必须要经过它(它不在最短路径上面),然后要有它的一条最短路径一定有跟原来最短路径前缀和后缀重复的部分。

我们记录 \(L_u\) 表示 \(1\righarrow u\) 的最晚在最短路径上面的边,\(R_u\) 反之,从 \(n\) 过来。

然后枚举这些边用线段树维护即可解决第二个部分的内容。

代码

时间复杂度 \(\mathcal{O}((n+q)\log n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#include <queue>
#define int long long
#define N 200006
#define isdigit(ch) ('0' <= ch && ch <= '9')
using namespace std;
template<typename T>
void read(T&x) {
	x = 0;
	char ch = getchar();
	for (;!isdigit(ch);ch = getchar());
	for (;isdigit(ch);ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
}
template<typename T>
void write(T x) {
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0'); 
}
int n,m,Q;
struct edge{
	int v,w,id;
};
vector<edge> g[N];
struct node{
	int u,dis;
	bool operator<(const node &adv) const {
		return adv.dis < dis;
	}
};
priority_queue<node> q;
int dis1[N],disn[N];
int lst_vis[N][2],in_dis[N],l[N],r[N];
bool vis[N],road[N];
void Dij(int s,int *dis,int type) {//1:1,2:n
	for (int i = 1;i <= n;i ++) dis[i] = 1e18,vis[i] = 0;
	q.push({s,dis[s] = 0});
	while(!q.empty()) {
		node t = q.top();
		q.pop();
		if (vis[t.u]) continue;
		vis[t.u] = 1;
		for (auto i : g[t.u])
			if (dis[i.v] > dis[t.u] + i.w) {
				dis[i.v] = dis[t.u] + i.w;
				lst_vis[i.v][0] = t.u,lst_vis[i.v][1] = i.id;
				q.push({i.v,dis[i.v]});
				if (type == 1 && !road[i.v]) l[i.v] = l[t.u];
				if (type == 2 && !road[i.v]) r[i.v] = r[t.u];
			}
	}
}
int dist;
void init() {
	int now = 1;
	road[now] = 1;
	l[now] = r[now] = 0;
	for (int i = 1;now != n;i ++) {
		int now_id = lst_vis[now][1];
		in_dis[now_id] = i;
		dist ++;
		now = lst_vis[now][0];
		road[now] = true;
		l[now] = r[now] = i;
	}
}
int tr[N << 2];
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)
void build(int x,int l,int r) {
	tr[x] = 8e18;
	if (l == r) return;
	int mid = l + r >> 1;
	build(ls(x),l,mid),build(rs(x),mid + 1,r);
}
void update(int x,int l,int r,int L,int R,int val) {
	if (l > R || r < L) return;
	if (L <= l && r <= R) return tr[x] = min(tr[x],val),void();
	int mid = l + r >> 1;
	update(ls(x),l,mid,L,R,val),update(rs(x),mid + 1,r,L,R,val);
}
int query(int x,int l,int r,int pos) {
	if (l == r) return tr[x];
	int res = tr[x];
	int mid = l + r >> 1;
	if (pos <= mid) res = min(res,query(ls(x),l,mid,pos));
	else res = min(res,query(rs(x),mid + 1,r,pos));
	return res;
}
int ww[N],uu[N],vv[N];
signed main(){
	read(n),read(m),read(Q);
	for (int i = 1;i <= m;i ++) {
		int u,v,w;
		read(u),read(v),read(w);
		g[u].push_back({v,w,i});
		g[v].push_back({u,w,i});
		in_dis[i] = -1;
		ww[i] = w;
		uu[i] = u,vv[i] = v;
	}
	Dij(n,disn,0);
	init();
	Dij(1,dis1,1);
	Dij(n,disn,2);
	build(1,1,dist);
	for (int i = 1;i <= m;i ++)
		if (in_dis[i] == -1)
			//the id of the edges,so we have to +1
			update(1,1,dist,l[uu[i]] + 1,r[vv[i]],dis1[uu[i]] + ww[i] + disn[vv[i]]),
			update(1,1,dist,l[vv[i]] + 1,r[uu[i]],dis1[vv[i]] + ww[i] + disn[uu[i]]);
	for (int id,x;Q --;) {
		read(id),read(x);
		int ans = dis1[n];
		if (in_dis[id] == -1 && x < ww[id])
			ans = min({ans,dis1[uu[id]] + x + disn[vv[id]],dis1[vv[id]] + x + disn[uu[id]]});
		else if (in_dis[id] != -1) {
			ans += x - ww[id];
			if (x > ww[id]) ans = min(ans,query(1,1,dist,in_dis[id])); 
		}
		write(ans);
		putchar('\n');
	}
	return 0;
}
posted @ 2026-07-22 22:21  high_skyy  阅读(2)  评论(0)    收藏  举报
动态线条
动态线条end
浏览器标题切换
浏览器标题切换end
💬 加载中……