【bzoj 4289】 tax(最短路)

文章目录

题意

一张无向图,经过一个点的代价是入边和出边的较大值,起点是出边的值,终点是入边的值。

求 1 到 n 的最短路。

思路

化边为点很显然。但是假如碰到菊花就萎了。

所以下面是一种挺神奇的思路。

  1. 把无向边拆成两个有向边。
  2. 每个点的所有出边排序,出边之间,代价小的向代价大的连边权为他们边权差的边,代价大的向小的连边权为 0 的边。
  3. 对于每一条边 i i i ,假如他从 u u u 走到 v v v ,找到 v v v 的出边中边权比 i i i 大的最小边,连边权差的边。找到边权比 i i i 小的最大边,连 0 的边。
  4. 最后再新建两个源点汇点,连向 1 和 n 。

这样的话大小关系就不用再判断了,只要跑正常的最短路就好了。

好像叫差分优化建边。我还没听说过。

代码

// bzoj 4289 tax
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<long long, int>
#define fi first
#define se second
#define mp make_pair
typedef long long LL;
const int N = 4e5 + 10, M = N<<2;
const LL inf = 1e18 + 7;
namespace Graph
{
	int h[N], ecnt, nxt[M], v[M];
	LL w[M];
	void clear(){ecnt = 1;}
	void add_dir(int _u, int _v, LL _w){
	    v[++ecnt] = _v; w[ecnt] = _w;
	    nxt[ecnt] = h[_u]; h[_u] = ecnt;
	}
}
using namespace Graph;
int n, m, s, t, beg[N], end[N];
struct edge{
	int x, y;
	LL z;
}e[N];
priority_queue<pii, vector<pii>, greater<pii> > que;
LL dis[N];

template<class T>inline void read(T &x){
	x = 0; bool fl = 0; char c = getchar();
	while (!isdigit(c)){if (c == '-') fl = 1; c = getchar();}
	while (isdigit(c)){x = (x<<3)+(x<<1)+c-'0'; c = getchar();}
	if (fl) x = -x;
}
template<class T>inline void wr(T x){
	if (x < 0) x = -x, putchar('-');
	if (x > 9) wr(x / 10);
	putchar(x % 10 + '0');
}
template<class T>inline void wrl(T x){
	wr(x); puts("");
}

bool cmp(edge x, edge y){
	if (x.x != y.x) return x.x < y.x;
	return x.z < y.z;
}

int get_id(int l, int r, LL val){
	int mid, ret = r+1;
	while (l <= r){
		mid = l+r>>1;
		if (e[mid].z >= val) ret = mid, r = mid-1;
		else l = mid+1;
	}
	return ret;
}

LL solve(){
	for (int i = 1; i <= m; ++ i) dis[i] = inf;
	dis[s] = 0; que.push(mp(dis[s], s));
	while (!que.empty()){
		int u = que.top().se;
		LL d = que.top().fi;
		que.pop();
		if (d > dis[u]) continue;
		for (int i = h[u]; i; i = nxt[i])
			if (dis[v[i]] > dis[u]+w[i]){
				dis[v[i]] = dis[u]+w[i];
				que.push(mp(dis[v[i]], v[i]));
			}
	}
	return dis[t];
}

signed main()
{
	read(n); read(m);
	for (int i = 1; i <= m; ++ i){
		int x, y, z;
		read(x); read(y); read(z);
		e[(i<<1)-1] = (edge){x, y, z};
		e[i<<1] = (edge){y, x, z};
	}
	m *= 2;
	e[++m] = (edge){n+1, 1, -inf};
	e[++m] = (edge){n, n+2, -inf};
	sort(e + 1, e + m + 1, cmp);
	clear();
	for (int i = 1, j; i <= m; i = j){
		for (j = i; j <= m && e[j].x == e[i].x; ++ j);
		int u = e[i].x;
		beg[u] = i; end[u] = j-1;
		for (int k = i+1; k < j; ++ k){
			add_dir(k-1, k, e[k].z-e[k-1].z);
			add_dir(k, k-1, 0);
		}
	}
	for (int i = 1; i <= m; ++ i){
		int v = e[i].y;
		int k = get_id(beg[v], end[v], e[i].z);
		if (v == n+2) t = i;
		if (e[i].x == n+1) s = i;
		if (k <= end[v]) add_dir(i, k, e[k].z);
		if (k > beg[v]) add_dir(i, k-1, e[i].z);
	}
	wrl(solve());
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值