[NOIP2017] 逛公園
阿新 • • 發佈:2018-02-23
給定 namespace 等於 urn space int tchar 記憶 記憶化 +\(d\) 的路徑有多少條。
我們假設\(u\) --> \(v\),\(d_u = rest'\) , \(d_v = rest\) , 那麽有如下關系:
\[rest' + dis[1 , u ] + t[ i ].lg\ \ =\ \ rest + dis[1 , v]\]
轉移:\(f[v][rest] = \sum f[ u ][rest']\) , 初值\(f[1][0] = 1\)
細節比較多 , 求 \(0\)環 判 -1 可以用\(Tarjan\)做,只走邊權為0的邊即可。
然後\(DP\)的時候轉移順序不好處理 , 所以記憶化搜索即可。
[NOIP2017] 逛公園
題目大意:
給定一張圖,詢問長度 不超過1到n的最短路長度加k 的1到n的路徑 有多少條。
數據範圍: 點數\(n \le 10^5\) ,邊數\(m \le 2*10^5\)
題目解法
兩個月後再看也不是太難,自己就能獨立思考出來。
首先是判-1的問題,顯然能產生-1的只有0環。
所以把0環都找出來,
然後檢查一下\(dis[\)\(1\),環\(]\) + \(dis[\)環,\(n]\) 是否小於等於 \(dis[1,n]+K\)即可。
如果不是無限路徑的話,也比較套路了。直接把距離扔到\(DP\)維數中肯定不現實。
所以設\(f[ u ][ d ]\)表示從1到u,長度為\(dis[1,u]\)
我們假設\(u\) --> \(v\),\(d_u = rest'\) , \(d_v = rest\) , 那麽有如下關系:
\[rest' + dis[1 , u ] + t[ i ].lg\ \ =\ \ rest + dis[1 , v]\]
轉移:\(f[v][rest] = \sum f[ u ][rest']\) , 初值\(f[1][0] = 1\)
細節比較多 , 求 \(0\)環 判 -1 可以用\(Tarjan\)做,只走邊權為0的邊即可。
然後\(DP\)的時候轉移順序不好處理 , 所以記憶化搜索即可。
實現代碼:
註:記憶化搜索倒著搜比較方便就倒著搜了。
#include<bits/stdc++.h> #define RG register #define IL inline #define os 55 #define _ 200005 #define INF 1000000007 using namespace std; IL int gi(){ RG int data = 0 , m = 1; RG char ch = 0; while(ch != '-' && (ch < '0' || ch > '9') ) ch = getchar(); if( ch == '-' ) { ch = getchar(); m = 0; } while(ch >= '0' && ch <= '9'){data = (data << 1) + (data << 3) + (ch ^ 48); ch = getchar();} return ( m ) ? data : -data; } int Case,N,M,K,P,zero_res,oo,top,cnt,ans,init[_]; int dis[_][2],dp[_][os],dfn[_],low[_],stk[_],tmp[_],hd[_]; struct Road{int to , next , w ; }t[2*_][ 2 ] ; int head[ _ ][ 2 ]; bool vis[_]; queue<int>Q; IL void spfa(RG int S , RG int id){ for(RG int i = 1; i <= N; i ++)dis[ i ][id] = INF ; vis[ S ] = true; Q.push( S ) ; dis[ S ][id] = 0; while(!Q.empty()){ RG int u = Q.front(); Q.pop(); for(RG int i = head[ u ][id] ; i ; i = t[ i ][id].next){ RG int v = t[ i ][id].to ; if(dis[ v ][id] > dis[ u ][id] + t[ i ][id].w){ dis[ v ][id] = dis[ u ][id] + t[ i ][id].w ; if(! vis[ v ] ) Q.push( v ) , vis[ v ] = true; } }vis[ u ] = false; }return; } IL void Tarjan( RG int u ){ stk[ ++ top ] = u; dfn[ u ] = low[ u ] = ++ oo ; init[ u ] = true; for(RG int i = head[ u ][0] ; i ; i = t[ i ][0].next){ if(t[ i ][0].w != 0)continue; RG int v = t[ i ][0].to; if( !dfn[ v ] ) Tarjan( v ) , low[ u ] = min(low[ u ] , low[ v ]) ; else if(init[ v ])low[ u ] = min(low[ u ] , dfn[ v ]) ; } if(low[ u ] == dfn[ u ]){ RG int e , ct = 0; while(1){ e = stk[ top ] ; top --; init[e] = false; tmp[ ++ct ] = e; if(e == u || !top) break; } if(ct >= 2) zero_res = min( dis[ u ][ 0 ] + dis[ u ][ 1 ] , zero_res) ; }return; } IL int DP( RG int u , RG int rest ) { if( dp[ u ][ rest ] )return dp[ u ][ rest ] ; for(RG int i = head[ u ][ 1 ] ; i ; i = t[ i ][ 1 ].next){ RG int v = t[ i ][ 1 ].to , d; d = rest + dis[ u ][ 0 ] - dis[ v ][ 0 ] - t[ i ][ 1 ].w ; if( d < 0 )continue; dp[ u ][ rest ] = ( dp[ u ][ rest ] + DP( v , d ) ) % P; }return dp[ u ][ rest ] ; } //v-->u : rest' + dis[v][0] + t[i].w = rest + dis[u][0] int main(){ freopen("2017park.in" , "r" , stdin); freopen("2017park.out" , "w" , stdout); Case = gi(); while(Case -- ){ N = gi(); M = gi(); K = gi(); P = gi(); for(RG int i = 1; i <= N; i ++)head[ i ][ 0 ] = 0; for(RG int i = 1; i <= N; i ++)head[ i ][ 1 ] = 0; for(RG int i = 1; i <= N; i ++)dfn[ i ] = low[ i ] = 0; cnt = 0; for(RG int i = 1 , u , v , c; i <= M; i ++){ u = gi(); v = gi(); c = gi(); ++ cnt ; t[ cnt ][ 0 ] = ( Road ) { v , head[ u ][ 0 ] , c } ; head[ u ][ 0 ] = cnt ; t[ cnt ][ 1 ] = ( Road ) { u , head[ v ][ 1 ] , c } ; head[ v ][ 1 ] = cnt ; //0 : u --> v (oder) // 1 : v --> u (dder) } spfa(1 , 0) ; spfa(N , 1) ; zero_res = INF ; for(RG int i = 1; i <= N ; i ++) if( ! dfn[ i ] ) Tarjan( i ) ; if(zero_res <= dis[ N ][ 0 ] + K){ puts("-1") ; continue; } for(RG int i = 1; i <= N; i ++) for(RG int j = 0; j <= K; j ++) dp[ i ][ j ] = 0; dp[ 1 ][ 0 ] = 1; ans = 0; for(RG int delta = 0; delta <= K; delta ++) ans = ( ans + DP( N , delta ) ) % P; cout << ans << endl; }return 0; }
[NOIP2017] 逛公園