服務器信息儲存(最短路)

服務器信息儲存
時間限制 : - 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也不會被別人喜歡(同情)。
讓人猶豫的是這樣隨之而來的時間複雜度的問題,無疑我們又把時間複雜度帶回了n2log2n 的起點。這道題的特殊點就在於題目限制了點對的數量爲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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章