洛谷P1261服务器储存信息问题

原题链接:https://www.luogu.com.cn/problem/P1261?contestId=60077

题意:

找到各个服务器感兴趣的服务器的总和。a对b感兴趣的条件为:不存在rank比b大且到a的距离比b小。

题解:

读完题,很明显,直接对每个点跑一遍spfa或者dijkstra,在用一个ans记录答案即可。但是会tle…
所以需要优化,根据rank进行优化是比较好的,因为rank数据小且与答案挂钩。rank的优化比较难想,具体证明如下:
首先,需要一个数组:d[i][j],此数组表示rank大于等于i的所有点到j点的最短距离,优化后的d我们可以直接拿来用,相当于 dist[j] < d[ra[s] + 1][j] 时,才能加入队列中,大量的降低了时间复杂度。
此等式怎么来的呢?
dist[j] = dist[t] + w[i]
首先,如上式,j点可以由t点更新时满足:dist[j] < d[ra[s] + 1][j](即当前j点到起点s的距离比所有rank大于s的最短距离小,证明该点为起点s感兴趣服务器)
所以,我们需要做两次spfa,第一次找到所有rank大于等于i的所有点到j点的最短距离,即求出d[i][j]。
第二次spfa便利用第一次求得的d[i][j]进行ans的记录。
ac代码如下:

#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<queue>
#include<deque>
#include<map>
#include<cstring>
#include<vector>
using namespace std;

const int N = 3e4 + 10, M = 3e5 + 10;
int ra[N];
int e[M], ne[M], w[M], h[N], idx;
int n, m;
int d[15][N], dist[N];
int ans;
bool st[N],nst[N];

void add(int a, int b, int c)
{
	e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

void spfa1(int x)
{
	for (int i = 0; i <= n; i++) d[x][i] = 1e9;
	memset(st, 0, sizeof st);
	queue<int>q;
	for (int i = 1; i <= n; i++)
	{
		if (ra[i] == x)
		{
			d[x][i] = 0;
			q.push(i);
		}
	}
	while (!q.empty())
	{
		int t = q.front();
		q.pop();
		st[t] = false;
		for (int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			if (d[x][j] > d[x][t] + w[i])
			{
				d[x][j] = d[x][t] + w[i];
				if (!st[j])
				{
					q.push(j);
					st[j] = true;
				}
			}
		}
	}
}

void spfa2(int s)
{
	memset(dist, 0x3f, sizeof dist);
	memset(st, 0, sizeof st);
	memset(nst, 0, sizeof nst);
	queue<int>q;
	q.push(s);
	dist[s] = 0;
	while (!q.empty())
	{
		int t = q.front();
		q.pop();
		st[t] = false;
		if (!nst[t])
		{
			ans++;
			nst[t] = true;
		}
		for (int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			if (dist[j] > dist[t] + w[i])
			{
				dist[j] = dist[t] + w[i];
				if (!st[j] && dist[j] < d[ra[s] + 1][j])
				{
					st[j] = true;
					q.push(j);
				}
			}
		}
	}
}

int main()
{
	cin >> n >> m;
	memset(h, -1, sizeof h);
	for (int i = 1; i <= n; i++)cin >> ra[i];
	for (int i = 0; i < m; i++)
	{
		int a, b, c;
		cin >> a >> b >> c;
		add(a, b, c), add(b, a, c);
	}
	for (int i = 1; i <= 10; i++) spfa1(i);
	for (int i = 9; i >= 1; i--)
	{
		for (int j = 1; j <= n; j++)
		{
			d[i][j] = min(d[i][j], d[i + 1][j]);
		}
	}
	for (int i = 1; i <= n; i++)spfa2(i);
	cout << ans << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ja_King_ZH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值