【luogu CF1163F】Indecisive Taxi Fee(图论)(分类讨论)

Indecisive Taxi Fee

题目链接:luogu CF1163F

题目大意

给你一个无向图,每次改一条边的权值(每次都会变回来),问你 1~n 的最短路长度。

思路

考虑分类讨论,先找到最短路的路径,然后看修改的边在不在最短路上。

如果不在,要么还是原来的最短路,要么就是你这条边 ( x , y ) (x,y) (x,y) 1 1 1 x x x 的最短路加上你的新边权再加上 y y y n n n 的最短路。
然后 1 , n 1,n 1,n 到所有点的最短路我们预处理出来就可以弄。

然后就是在最短路,那要么是原来的最短路优,要么是你新找一条最短路它不经过你改的这条边(就是把它删了跑最短路)。
发现只有这个新找路要求,考虑一下这条路的性质。

发现一定能找一条,它肯定是跟最短路有一个前缀,一个后缀,中间的部分不会跟最短路的边有交集。
因为有交集,假设分成两半,你把不是删了的边的那一半改成最短路,那更短或者一样啊。

然后你就看每条不在最短路上的边,如果要他走这条边作为最短路,它能代替那些最短路上的边。
那根据前面,代替的也是一个区间的。
于是考虑求这个区间。

对于每个点,你有一个 L i L_i Li 表示最小的 x x x,使得 1 → i 1\rightarrow i 1i 的路上 e x e_x ex 这条边是第一个不在最短路上的边
e i e_i ei 是最短路的边)
R i R_i Ri 表示最大的 x x x 使得 x → n x\rightarrow n xn 的路上 e x e_x ex 这条边是最后一个不在最短路上的边。
那这个我们可以 DP,一开始对于最短路上的点,有 L x = i , R x = i L_x=i,R_x=i Lx=i,Rx=i i i i x x x 是第几个点)

然后你按从 1 1 1 开始的最短路树的顺序,和 n n n 开始的最短路树的顺序,分别 DP L i , R i L_i,R_i Li,Ri,就直接选不在最短路上的边,儿子跟它取 min ⁡ , max ⁡ \min,\max min,max 即可。
那求出来点,接着看边的,那就根据 L x , R y L_x,R_y Lx,Ry 确定一下 x , y x,y x,y,只要有区间就行那种,那这个就是那个区间了。
那怎么求所有边的答案呢?因为有很多个区间可以选。
会发现对于里面能替换掉的所有最短路上的边,替换之后走的距离都是一样的。

然后你考虑一个指针从左往右扫,最左侧放入,最右侧弹出,维护一个 multiset,每次答案就是最小值。
当然你用线段树搞也可以。

代码

#include<set>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm> 
#define ll long long
using namespace std;

const ll N = 2e5 + 1000;
struct node {
	ll x, to, nxt, id;
}e[N << 1];
ll n, m, q, le[N], KK, dian[N], bian[N];
ll xxx[N], yyy[N], zzz[N], pl[N], ib[N];
ll L[N], R[N];
ll miss[N];

void add(ll x, ll y, ll z, ll id) {
	e[++KK] = (node){z, y, le[x], id}; le[x] = KK;
}

bool cant[N];
struct Work {
	ll S, fr[N], frid[N];
	ll f[N];
	bool in[N];
	
	void work() {
		priority_queue <pair<ll, ll>, vector<pair<ll, ll> >, greater<pair<ll, ll> > > q;
		while (!q.empty()) q.pop(); memset(in, 0, sizeof(in));
		memset(f, 0x3f, sizeof(f)); f[S] = 0; q.push(make_pair(0, S));
		while (!q.empty()) {
			ll now = q.top().second; q.pop();
			if (in[now]) continue; in[now] = 1;
			for (ll i = le[now]; i; i = e[i].nxt)
				if (!cant[e[i].id] && f[e[i].to] > f[now] + e[i].x) {
					f[e[i].to] = f[now] + e[i].x; fr[e[i].to] = now; frid[e[i].to] = e[i].id;
					q.push(make_pair(f[e[i].to], e[i].to));
				}
		}
	}
	
	void get_road(ll T) {
		ll now = T;
		while (now != S) {
			cant[frid[now]] = 1; bian[++bian[0]] = frid[now]; dian[++dian[0]] = now; now = fr[now];
		}
		dian[++dian[0]] = now; reverse(dian + 1, dian + dian[0] + 1);
		reverse(bian + 1, bian + bian[0] + 1);
	}
}T1, Tn;

bool cmp1(ll x, ll y) {
	return T1.f[x] < T1.f[y];
}
bool cmpn(ll x, ll y) {
	return Tn.f[x] < Tn.f[y];
}
multiset <ll> qq;
vector <ll> up[N], down[N];

int main() {
	scanf("%lld %lld %lld", &n, &m, &q);
	for (ll i = 1; i <= m; i++) {
		ll x, y, z; scanf("%lld %lld %lld", &x, &y, &z);
		add(x, y, z, i); add(y, x, z, i);
		xxx[i] = x; yyy[i] = y; zzz[i] = z;
	}
	
	T1.S = 1; Tn.S = n;
	T1.work(); Tn.work();
	T1.get_road(n);
	
	for (ll i = 1; i <= bian[0]; i++) ib[bian[i]] = i;
	memset(L, 0x3f, sizeof(L));
	for (ll i = 1; i <= dian[0]; i++) L[dian[i]] = i, R[dian[i]] = i;
	for (ll i = 1; i <= n; i++) pl[i] = i;
	sort(pl + 1, pl + n + 1, cmp1);
	for (ll i = 1; i <= n; i++) {
		ll now = pl[i];
		for (ll j = le[now]; j; j = e[j].nxt)
			if (!cant[e[j].id] && T1.f[e[j].to] == T1.f[now] + e[j].x)
				L[e[j].to] = min(L[e[j].to], L[now]);
	}
	sort(pl + 1, pl + n + 1, cmpn);
	for (ll i = 1; i <= n; i++) {
		ll now = pl[i];
		for (ll j = le[now]; j; j = e[j].nxt)
			if (!cant[e[j].id] && Tn.f[e[j].to] == Tn.f[now] + e[j].x)
				R[e[j].to] = max(R[e[j].to], R[now]);
	}
	for (ll i = 1; i <= m; i++) if (!cant[i]) {
		ll x = xxx[i], y = yyy[i];
		if (L[x] <= R[y] - 1) {
			ll val = T1.f[x] + zzz[i] + Tn.f[y];
			up[L[x]].push_back(val); down[R[y] - 1].push_back(val);
		}
		swap(x, y);
		if (L[x] <= R[y] - 1) {
			ll val = T1.f[x] + zzz[i] + Tn.f[y];
			up[L[x]].push_back(val); down[R[y] - 1].push_back(val);
		}
	}
	memset(miss, 0x3f, sizeof(miss)); 
	for (ll i = 1; i <= dian[0]; i++) {
		for (ll j = 0; j < up[i].size(); j++) qq.insert(up[i][j]);
		if (!qq.empty()) miss[i] = *qq.begin();
		for (ll j = 0; j < down[i].size(); j++) qq.erase(qq.find(down[i][j]));
	}
	
	while (q--) {
		ll x, y; scanf("%lld %lld", &x, &y);
		if (cant[x]) {//在最短路 
			printf("%lld\n", min(miss[ib[x]], T1.f[n] - zzz[x] + y));
		}
		else {//不在最短路 
			printf("%lld\n", min(T1.f[n], min(T1.f[xxx[x]] + y + Tn.f[yyy[x]], T1.f[yyy[x]] + y + Tn.f[xxx[x]])));
		}
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值