服务器信息储存(最短路)

服务器信息储存
时间限制 : 1000 MS 空间限制 : 265536 KB
问题描述

B y t e l a n d Byteland 王国准备在各服务器间建立大型网络并提供多种服务。

网络由n台服务器组成,用双向的线连接。两台服务器之间最多只能有一条线直接连接,同时,每台服务器最多只能和10台服务器直接连接,但是任意两台服务器间必然存在一条路径将它们连接在一起。每条传输线都有一个固定传输的速度。δ(V, W)表示服务器V和W之间的最短路径长度,且对任意的V有δ(V, V)=0。

有些服务器比别的服务器提供更多的服务,它们的重要程度要高一些。我们用r(V)表示服务器V的重要程度(rank)。rank越高的服务器越重要。

每台服务器都会存储它附近的服务器的信息。当然,不是所有服务器的信息都存,只有感兴趣的服务器信息才会被存储。服务器V对服务器W感兴趣是指,不存在服务器U满足,r(U)>r(W)且δ(V, U)<=δ(V, W)。

举个例子来说,所有具有最高rank的服务器都会被别的服务器感兴趣。如果V是一台具有最高rank的服务器,由于δ(V, V)=0,所以V只对具有最高rank的服务器感兴趣。我们定义B(V)为V感兴趣的服务器的集合。

我们希望计算所有服务器储存的信息量,即所有服务器的|B(V)|之和。Byteland王国并不希望存储大量的数据,所以所有服务器存储的数据量(|B(V)|之和)不会超过30n。

你的任务是写一个程序,读入Byteland王国的网络分布,计算所有服务器存储的数据量。

输入格式

第一行两个整数n和m,(1≤n≤30000,1≤m≤5n)。n表示服务器的数量,m表示传输线的数量。

接下来n行,每行一个整数,第i行的整数为r(i)(1≤r(i)≤10),表示第i台服务器的rank。

接下来m行,每行表示各条传输线的信息,包含三个整数a,b,t(1≤t≤1000,1≤a,b≤n,a≠b)。a和b是传榆线所连接的两台服务器的编号,t是传输线的长度。

输出格式

一个整数,表示所有服务器存储的数据总量,即|B(V)|之和。

样例输入

4 3
2
3
1
1
1 4 30
2 3 20
3 4 20

样例输出

9

提示

样例解释

B(1)={1,2},B(2)={2},B(3)={2,3},B(4)={1,2,3,4}。

数据范围

对于 30%的数据,n≤100,m<=300。

对于 60%的数据,n≤1000,m<=20000。

对于 100%的数据, 1≤n≤30000,1≤m≤5n

%OB%shenben
n=30000是比较棘手的情况。
于是考虑如何简化题目。最高rank的服务器被所有的喜欢是一个很有用的提示:对于当前的节点p,是否喜欢另一个节点v,只需要知道mindis( p -> rank[v] + 1 ) 于是这里就可以预处理出每一个点到特定rank点集的最短路。
然后对于每一个点跑最短路,求出到每一个点的最短路,并与对于该rank的限制条件相对比( mindis[p][rank[v]] ), 如果当前节点p不对v感兴趣,那么由v扩展出去的节点w也不会被别人喜欢(同情)。
让人犹豫的是这样随之而来的时间复杂度的问题,无疑我们又把时间复杂度带回了 n 2 l o g 2 n n^2log_2n 的起点。这道题的特殊点就在于题目限制了点对的数量为30n,所以我们的实际运行时间并不会太长。

#include <bits/stdc++.h>
using namespace std;
inline int R(){
	char t=getchar();int o=0;bool F=0;
	while(t<48||t>57)F|=t==45,t=getchar();
	for(;t<58&&t>47;t=getchar())o=(o<<1)+(o<<3)+t-48;
	return F?-o:o;
}
inline int Min(int a,int b){
	int m = (a-b) >> 31;
	return a&m|b&~m;
}
const int N = 30005, M = 300005;
int cnt_edge = 1, Last[N], End[M], Next[M], Len[M];
int dis[N][10], n, m, r[N], cnt[11];
int d[N][10], D[N], id[N], ans;
inline void add_edge( int a, int b, int c ){
	Next[++cnt_edge] = Last[a];
	Last[a] = cnt_edge;
	End[cnt_edge] = b;
	Len[cnt_edge] = c;
}

priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > q;

void dijkstra( int s ){
	for( int i = 1; i <= n; i ++ )
		if( r[i] == s ){
			dis[i][s] = 0;
			q.push( make_pair( 0, i ) );
		}
	int x, v;
	while( ! q.empty() ){
		x = q.top().second; v = q.top().first;
		q.pop();
		if( dis[x][s] != v ) continue;
		for( int i = Last[x], y = End[i]; i; i = Next[i], y = End[i] )
			if( dis[y][s] > dis[x][s] + Len[i] ){
				dis[y][s] = dis[x][s] + Len[i];
				q.push( make_pair( dis[y][s], y ) );
			}
	}
}

void Dijks( int s ){
	
	//666这个常数优化,感觉和匈牙利求二分图增广路的想法很相似,不用清零dis了
	D[s] = 0; q.push( make_pair( 0, s ) ); id[s] = s;
	int x, v, i, y;
	while( ! q.empty() ){
		x = q.top().second; v = q.top().first; q.pop();
		if( D[x] != v ) continue;
		if( v < d[x][r[s]] ) ans ++;
		else continue;
		for( i = Last[x], y = End[i]; i; i = Next[i], y = End[i] )
			if( id[y] != s || D[y] > v + Len[i] ){
				D[y] = v + Len[i];
				if( id[y] != s ) id[y] = s;
				q.push( make_pair( D[y], y ) );
			}
	}
}


int main()
{
	n = R(); m = R();
	for( int i = 1; i <= n; i ++ ) r[i] = R(), cnt[r[i]] ++;
	
	for( int i = 1; i <= m; i ++ ){
		int a = R(), b = R(), c = R();
		add_edge( a, b, c );
		add_edge( b, a, c );
	}
	memset( dis, 120, sizeof( dis ) );
	for( int i = 1; i < 11; i ++ )
		if( cnt[i] )
			dijkstra( i );
	
	for( int i = 1; i <= n; i ++ ){
		d[i][10] = 0x3fffffff;
		for( int j = 9; j >= 1; j -- )
			d[i][j] = Min( d[i][j+1], dis[i][j+1] );
	}
	
	for( int i = 1; i <= n; i ++ )
		Dijks( i );
	cout << ans;
	return 0;
}
发布了11 篇原创文章 · 获赞 5 · 访问量 3378

猜你喜欢

转载自blog.csdn.net/qq_24855707/article/details/77927882