zoukankan      html  css  js  c++  java
  • [NOIP2017]逛公园

    输入输出样例

    输入样例#1:

    2
    5 7 2 10
    1 2 1
    2 4 0
    4 5 2
    2 3 2
    3 4 1
    3 5 2
    1 5 3
    2 2 0 10
    1 2 0
    2 1 0

    输出样例#1:

    3
    -1


    联赛题好难啊

    但是看到(k<=50)是不是就能想到把k压入状态?

    (f[i][j])表示到i点已经比到i点的最短路远了j的路径条数

    好像记搜比较好写,但是我写了个DP

    就是类似于分层图的思想

    先枚举j,因为j只有50层,所以可以分层来转移

    如果不分层的话可能用来更新ta的状态可能后来还有变化

    然后再按照dis的顺序呢更新

    因为如果不按照dis的顺序

    用来更新ta的状态后来还有变化

    会造成答案较小

    这样就可以得到70分辣

    然后我们考虑一下带有0边时应该怎么处理

    首先如果形成0环并且有一个0环上的点在一条合法的路径上(路径长度(<_{min} dis_{1~n} + k))

    就有无数条路径(因为ta可以在这条路径上一直转圈)

    然后如果只是有0边但没有形成0环

    还是按照我们刚才的顺序更新答案的话会出现问题

    因为dis会出现重复的情况

    但是我们在dis相同时应该先更新非0边上的点

    因为拓扑序大的一定是由拓扑序小的走过来的,如果不按照拓扑序更新仍然会导致用来更新的状态后来会有变化

    所以我们可以用拓扑排序来实现

    拓扑排序只连0边方便处理

    这样我们就按照dis为第一关键字,拓扑序为第二关键字为顺序更新就可以辣

    然后就是我懒得写dijkstra就写了那个死掉的算法

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    const int M = 100005 ;
    const int N = 55 ;
    using namespace std ;
    inline int read() {
        char c = getchar() ; int x = 0 , w = 1 ;
        while(c>'9'||c<'0') { if(c=='-') w = - 1 ; c = getchar() ; }
        while(c>='0' && c<='9') { x = x*10+c-'0' ; c = getchar() ; }
        return x*w ;
    }
    struct E {
        int Nxt , to , Dis ;
    } edge [M<<1] , rev [M<<1] ; 
    int hea [M] , rhea[M] , num , rnum ;
    inline void add_edge(int from , int to , int dis) {
        edge[++ num].Nxt = hea[from] ; edge[num].to = to ;
        edge[num].Dis = dis ; hea[from] = num ;
    
        rev[++ rnum].Nxt = rhea[to] ; rev[num].to = from ;
        rev[num].Dis = dis ; rhea[from] = rnum ;
    }
    
    int n , m , k , p , Ans ;
    int f[M][N] , Dist[M] , Disf[M] , c[M] ;
    bool exist[M] , vis[M] ;
    struct Node { int Id , Dis , dep ; } d[M] ;
    inline bool operator < (Node a , Node b) { return a.Dis == b.Dis ? a.dep < b.dep : a.Dis < b.Dis ; }
    inline void Tspfa(int T) {
        memset(exist , false , sizeof(exist)) ;
        memset(Dist , 63 , sizeof(Dist)) ;
        queue < int > q ;
        Dist[T] = 0 ; q.push(T) ;
        while(!q.empty()) {
            int u = q.front() ; q.pop() ; exist[u] = false ;
            for(int i = rhea[u] ; i ; i = rev[i].Nxt) {
                int v = rev[i].to ;
                if(Dist[v] > Dist[u] + rev[i].Dis) {
                    Dist[v] = Dist[u] + rev[i].Dis ;
                    if(!exist[v]) q.push(v) , exist[v] = true ;
                }
            }
        }
    }
    inline void Sspfa(int S) {
        memset(exist , false , sizeof(exist)) ;
        memset(Disf , 63 , sizeof(Disf)) ;
        queue< int > q ; 
        Disf[S] = 0 ; q.push(S) ;
        while(!q.empty()) {
            int u = q.front() ; q.pop() ; exist[u] = false ;
            for(int i = hea[u] ; i ; i = edge[i].Nxt) {
                int v = edge[i].to ;
                if(Disf[v] > Disf[u] + edge[i].Dis) {
                    Disf[v] = Disf[u] + edge[i].Dis ;
                    if(!exist[v]) q.push(v) , exist[v] = true ;
                }
            }
        }
    }
    struct EDDE { int Nxt , to ; }Edge[M];
    int Hea[M] , Num ;
    inline void Insert(int from , int to) {Edge[++Num].Nxt = Hea[from] ; Edge[Num].to = to ; Hea[from] = Num ; } 
    inline bool Check() {
        queue < int > q ; int tot = 0 ;
        for(int i = 1 ; i <= n ; i ++)
          if(c[i] == 0) q.push(i) , vis[i] = true , -- c[i] , d[i].dep = ++tot ; ;
        while(!q.empty()) {
        	int u = q.front() ; q.pop() ;
        	for(int i = Hea[u] ; i ; i = Edge[i].Nxt) {
        		int v = Edge[i].to ; --c[v] ;
                if(c[v] == 0) { vis[v] = true ; q.push(v) ; d[v].dep = ++tot ; } 
            }
        }
        for(int i = 1 ; i <= n ; i ++)
            if(!vis[i])
                if(Dist[i] + Disf[i] <= Disf[n] + k)
                    return false ;
        return true ;
    }
    inline void Clear() {
        Ans = 0 ; memset(f , 0 , sizeof(f)) ; memset(c , 0 , sizeof(c)) ;
        memset(vis , 0 , sizeof(vis)) ; memset(hea , 0 , sizeof(hea)) ; num = 0 ;
        memset(rhea , 0 , sizeof(rhea)) ; rnum = 0 ; 
        memset(d , 0 , sizeof(d)) ; memset(Hea , 0 , sizeof(Hea)) ; Num = 0 ;
    }
    int main() {
        int T = read() ;
        while(T -- ) {
            Clear() ;
            n = read() ; m = read() ; k = read() ; p = read() ;
            for(int i = 1 , u , v , w ; i <= m ; i ++) { 
                u = read() , v = read() , w = read() ; add_edge(u , v , w) ;
                if(w == 0) ++c[v] , Insert(u , v) ;
            }
            Tspfa(n) ; Sspfa(1) ;
            if(!Check()) { printf("-1
    ") ; continue ; }
            for(int i = 1 ; i <= n ; i ++) d[i].Id = i , d[i].Dis = Disf[i] ;
            sort(d + 1 , d + n + 1) ;
            f[1][0] = 1 ;
            for(int j = 0 ; j <= k ; j ++)
                for(int i = 1 ; i <= n ; i ++) {
                	int u = d[i].Id , dis = d[i].Dis ;
                	for(int l = hea[u] ; l ; l = edge[l].Nxt) {
                		int v = edge[l].to ;
                		if(Disf[u] + j + edge[l].Dis - Disf[v] > k) continue ;
                		f[v][Disf[u] + j + edge[l].Dis - Disf[v]] = ( f[v][Disf[u] + j + edge[l].Dis - Disf[v]] + f[u][j] )%p ;
                    }
                }
            for(int i = 0 ; i <= k ; i ++) Ans = (Ans + f[n][i]) % p ;
            printf("%d
    ",Ans) ;
        }
        return 0 ;
    }
    

    upd : 又去写了一发记搜,感觉记搜比DP好写多了

    就是设(f[i][j])表示到点i时与从1到i的最短路径距离差<=j时的路径数

    然后就是直接用 (revdis[v] - revdis[u] + edge[i].dis)表示此次的路径偏差

    然后如何判断0环呢?

    如果在更新这种状态的时候又碰到相同的状态

    那么就一定是0环了

    记搜真好写

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    const int M = 100005 ;
    const int N = 55 ;
    using namespace std ;
    inline int read() {
    	char c = getchar() ; int x = 0 , w = 1 ; while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    	while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; } return x*w ;
    }
    
    
    int n , m , k , p ;
    int rhea[M] , hea[M] , rnum , num , Dist[M] , f[M][N] , Ans ;
    bool exist[M] , vis[M][N] ;
    struct E { int Nxt , to , Dis ; } rev[M<<1] , edge[M<<1] ;
    struct Node { int Id , Dis ; };
    inline bool operator < (Node a , Node b) { return a.Dis > b.Dis ; }
    inline void add_edge(int from , int to , int dis) {
    	edge[++num].Nxt = hea[from] ; edge[num].to = to ; edge[num].Dis = dis ; hea[from] = num ;
    	rev[++rnum].Nxt = rhea[to] ; rev[rnum].to = from ; rev[rnum].Dis = dis ; rhea[to] = rnum ;
    }
    inline void DijkstraT(int T) {
    	priority_queue< Node > q ; q.push((Node){T , 0}) ;
    	memset(exist , false , sizeof(exist)) ; Dist[T] = 0 ;
    	while(!q.empty()) {
    		int u = q.top().Id ; q.pop() ; if(exist[u]) continue ; exist[u] = true ;
    		for(int i = rhea[u] , v ; i ; i = rev[i].Nxt) { 
    		    v = rev[i].to ;
    			if(Dist[v] > Dist[u] + rev[i].Dis) Dist[v] = Dist[u] + rev[i].Dis , q.push((Node){ v , Dist[v] }) ;
    		}
    	}
    }
    int Dfs(int u , int res) {
    	if(vis[u][res]) return - 1 ; if(f[u][res]) return f[u][res] ;
    	vis[u][res] = true ; if(u == n) f[u][res] = 1 ;
    	for(int i = hea[u] , v , w , c ; i ; i = edge[i].Nxt) {
    		v = edge[i].to ; c = Dist[v] - Dist[u] + edge[i].Dis ; if(c > res) continue ;
    	    w = Dfs(v , res - c) ; if(w == -1) return f[u][res] = -1 ; f[u][res] = (f[u][res] + w)%p ;
    	}
    	vis[u][res] = false ; return f[u][res] ;
    }
    inline void Clear() {
    	memset(hea , 0 , sizeof(hea)) ; num = 0 ; memset(rhea , 0 , sizeof(rhea)) ; rnum = 0 ;
    	memset(f , 0 , sizeof(f)) ;  memset(Dist , 63 , sizeof(Dist)) ; memset(vis , 0 , sizeof(vis)) ;
    }
    int main() {
    	int T = read() ;
    	while(T -- ) {
    		Clear() ; n = read() ; m = read() ; k = read() ; p = read() ;
    		for(int i = 1 , u , v , w ; i <= m ; i ++) { u = read() , v = read() , w = read() ; add_edge(u , v , w) ; }
    		DijkstraT(n) ;
    		cout << Dfs(1 , k) << endl ; 
    	}
    	return 0 ;
    }
    
  • 相关阅读:
    java生成验证码
    springmvc笔记(来自慕课网)
    angularJs编写多指令的情况
    四年前端开发的迷茫.
    angularJs的ui-router总结
    grunt构建前端自动化的开发环境
    socket传送文件
    socket--粘包
    socket--接受大数据
    动态导入模块
  • 原文地址:https://www.cnblogs.com/beretty/p/9587796.html
Copyright © 2011-2022 走看看