传送门:【Codeforces】449B Jzzhu and Cities
题目大意:一个n个节点的无向图,节点编号1~n(其中1为起点),其中有m条普通普通,还有k条从起点出发的特殊边,问最多去掉多少的特殊边使得从起点到其他所有点的最短路径的距离不变。
题目分析:这题的意图很明显啊,就是要我们去求最短路啊。那么怎么求会比较好呢?其实我们可以很容易的发现如果从起点通过边权为c的特殊边到达顶点x,如果起点到x的最短路长度l < c,那么说明这条特殊边是可以去掉的。
什么,去掉会影响别的顶点的最短路?一定不会的啦,以为所有最短路径包括x的都可以用长度为l的路径来代替起点到x而不是比l长的这条特殊边。当然这是假设从起点到x没有特殊边,我们才可以用l代替c,那么如果起点到x的路径中包括了特殊边怎么办?
那么就变成一个子问题了:从起点到x的路径上y的最短路径判断的问题了。那么有没有可能绕了一圈又回到x?显然不可能,因为这样只可能最短路上的其他边的边权均为0(但是这个已经被题目限制了),而且这样l = c与一开始的条件矛盾。
好了,现在我们已经知道如果特殊边比最短路长那么就可以不选,如果l = c呢?我们该不该选呢?现在,假如我们记下了从起点到其他所有顶点的最短路的条数,当1->x的最短路条数只有1条的时候,那么那条最短路一定就是这条特殊边了,如果条数大于1,说明可以通过别的途径到达x,那么能不选特殊边就不选,何乐而不为呢?
很明显l>c是不可能出现的。怎么会出现最短路长度比一条能够直接到达x的边还长,你说是吧。
现在我们来分析一下特殊情况:如果同时有两条边权相同的特殊边连接x,并且除去他们最短路会变长,那么按照上面的方法判定,很明显,两条都会被删除!
那么我们该怎么避免?做法很简单,只要一开始预处理一下,对与一个顶点我们只保留一条特殊边就够了~
好了。。。唠叨了好多啊。。。。总体思路就是这样,2A,为什么是2A呢?因为最短路条数计数的时候会出现一个问题:可能条数过多就爆int了(爆long long 也是轻而易举),所以最后我特殊判断了一下,如果条数大于2,那么直接等于2就好了。。然后就过了。。。
代码如下:
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #define REP( i , n ) for ( int i = 0 ; i < n ; ++ i ) #define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i ) #define clear( a , x ) memset ( a , x , sizeof a ) typedef long long LL ; const int MAXN = 100005 ; const int MAXE = 800005 ; const int MAXH = 800005 ; const int oo = 0x3f3f3f3f ; const LL INF = 1e15 ; struct Edge { int v , n ; int c ; Edge () {} Edge ( int var , int cost , int next ) : v ( var ) , c ( cost ) , n ( next ) {} } ; struct Node { int idx ; LL c ; Node () {} Node ( LL cost , int _idx ) : c ( cost ) , idx ( _idx ) {} bool operator < ( const Node &a ) const { return c < a.c ; } } ; struct priority_queue { Node heap[MAXH] ; int point ; void init () { point = 1 ; } void push ( LL c , int idx ) { heap[point] = Node ( c , idx ) ; int o = point ++ ; while ( o > 1 && heap[o] < heap[o >> 1] ) swap ( heap[o] , heap[o >> 1] ) , o >>= 1 ; } int empty () { return point == 1 ; } Node top () { return heap[1] ; } int front () { return heap[1].idx ; } void pop () { heap[1] = heap[-- point] ; int o = 1 , p = o , l = o << 1 , r = o << 1 | 1 ; while ( o < point ) { if ( l < point && heap[l] < heap[p] ) p = l ; if ( r < point && heap[r] < heap[p] ) p = r ; if ( p == o ) break ; swap ( heap[o] , heap[p] ) ; o = p , l = o << 1 , r = o << 1 | 1 ; } } } ; struct Dij { priority_queue q ; Edge edge[MAXE] ; int adj[MAXN] , cntE ; LL d[MAXN] ; bool done[MAXN] ; int cnt[MAXN] ; int dir[MAXN] ; int NV , NE , K ; int tot ; void init () { cntE = 0 ; clear ( dir , oo ) ; clear ( adj , -1 ) ; } void addedge ( int u , int v , int c ) { edge[cntE] = Edge ( v , c , adj[u] ) ; adj[u] = cntE ++ ; edge[cntE] = Edge ( u , c , adj[v] ) ; adj[v] = cntE ++ ; } void dijkstra () { q.init () ; REP ( i , NV + 1 ) d[i] = INF ; clear ( done , 0 ) ; clear ( cnt , 0 ) ; d[1] = 0 ; cnt[1] = 1 ; q.push ( d[1] , 1 ) ; while ( !q.empty () ) { int u = q.front () ; q.pop () ; if ( done[u] ) continue ; done[u] = 1 ; for ( int i = adj[u] ; ~i ; i = edge[i].n ) { int v = edge[i].v , c = edge[i].c ; if ( d[v] > d[u] + c ) { d[v] = d[u] + c ; cnt[v] = cnt[u] ; q.push ( d[v] , v ) ; } else if ( d[v] == d[u] + c ) { cnt[v] += cnt[u] ; if ( cnt[v] > 1 ) cnt[v] = 2 ; } } } } void read ( int &x ) { x = 0 ; char c = ' ' ; while ( c < '0' || c > '9' ) c = getchar () ; while ( c >= '0' && c <= '9' ) x = x * 10 + c - '0' , c = getchar () ; } void input () { int u , v , c ; tot = 0 ; REP ( i , NE ) { read ( u ) , read ( v ) , read ( c ) ; addedge ( u , v , c ) ; } REP ( i , K ) { read ( v ) , read ( c ) ; if ( dir[v] > c ) dir[v] = c ; } REPF ( i , 2 , NV ) if ( dir[i] != oo ) { addedge ( 1 , i , dir[i] ) ; ++ tot ; } } void solve () { init () ; input () ; dijkstra () ; REPF ( i , 2 , NV ) if ( dir[i] != oo ) if ( dir[i] > d[i] || dir[i] == d[i] && cnt[i] > 1 ) -- tot ; printf ( "%d\n" , K - tot ) ; } } ; Dij z ; int main () { while ( ~scanf ( "%d%d%d" , &z.NV , &z.NE , &z.K ) ) z.solve () ; return 0 ; }