11月5日晚模拟赛题解

T1 赛艇表演

题意:\(n\)个点,\(m\)条边,每个点有权值,每条边也有权值(按读入$\times$2处理),对每个点,选定一个目标点(可以是它自己),使这个点到达目标点的距离加上目标点的权值的和最小。

\(1 <= n, m<=2e5\)

解法:

\(dijkstra\) + 超级源点

这个数据范围不允许我们跑\(floyd\)或者\(n\)\(dijkstra\),但我们发现,对于每个点,只需求一个最小值,并且数据范围其实也告诉我们了只需要跑一次\(dijkstra\),那么我们该从那个点跑\(dijkstra\)呢?我们发现很难从图中找到这样的一个点,使得所有的点都和它有关。

我们再梳理一下思路,我们需要找到一个点,以TA为起点跑一遍单源最短路,并且我们要使得所有的点都和它有关。

我们发现这个点在图中无法找到,所以我们选择建立一个超级源点,让它想所有的点建一条长度为点的权值的边,然后从它开始跑一遍\(dijkstra\)。仔细思考一番,我们发现这个算法的正确性是对的,时间复杂度\(O(nlogn)\)

#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
#define maxn 200100
#define pr pair<ll, int> 
#define mp make_pair 
#define ll long long
int fir[maxn], nxt[maxn * 3], vv[maxn * 3];
ll edg[maxn * 3];
int tot = 0, n, m;
void add(int u, int v, ll w)
{
	nxt[++tot] = fir[u];
	fir[u] = tot; 
	vv[tot] = v;
	edg[tot] = w;
}
ll dis[maxn], vis[maxn], val[maxn];
priority_queue<pr, vector<pr>, greater<pr> >q;
ll dijkstra(int x)
{
	for(int i = 1; i <= n + 1; i++) { dis[i] = 1e9; dis[i] *= 1e9; }
	memset(vis, 0, sizeof(vis));
	dis[x] = 0;
	q.push(mp(dis[x], x));
	while(!q.empty())
	{
		pr tmp = q.top(); q.pop();
		int u = tmp.second;
		if(vis[u] == 1) continue;
		vis[u] = 1;
		for(int i = fir[u]; i; i = nxt[i])
		{
			int v = vv[i];
			if(dis[v] > dis[u] + edg[i])
			{
				dis[v] = dis[u] + edg[i];
				q.push(mp(dis[v], v));
			}
		}
	}
	ll ans = 1e9; ans *= 1e9;
	for(int i = 1; i <= n; i++) printf("%d ", dis[i]);
	return ans;
}
int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; i++)
	{
		int u, v; ll w; scanf("%d%d%lld", &u, &v, &w);
		add(u, v, w * 2); add(v, u, w * 2);
	}
	for(int i = 1; i <= n; i++) scanf("%lld", &val[i]);
	for(int i = 1; i <= n; i++) add(n + 1, i, val[i]);
	dijkstra(n + 1);
	return 0;
}

T2 逮虾户

这是一道做法很简单的实数二分,但要注意\(l,r\)的取值范围,\(r\)最大可以取到\(1e6 +1e3\)

同时,要注意\(double\)的精度范围,\(double\)的有效范围是\(15\)\(到16\)位,\(long\,\,double\)的有效范围是\(18\)\(19\)位,如果暴范围了,就很有可能\(l,r\)变成\(0\),然后\(T\)掉。

T3 战略威慑

这是一道很水的枚举删边,然后求树的直径的题,略过。

posted @ 2019-11-06 13:55  Akaina  阅读(116)  评论(0编辑  收藏  举报