服務器信息儲存
時間限制 : - MS 空間限制 : 265536 KB
評測說明 : 1000ms
問題描述
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也不會被別人喜歡(同情)。
讓人猶豫的是這樣隨之而來的時間複雜度的問題,無疑我們又把時間複雜度帶回了
#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;
}