zoukankan      html  css  js  c++  java
  • [洛谷P3953] 逛公园

    洛谷题目链接:逛公园

    题目复制过来有点炸格式就不贴了,看题目请戳上面的链接

    题意: 给出一张(n)个点,(m)条边的有向图,设(1)(n)的最短路径长度为(d),问(1)(n)路径长度小于等于(d+k)的路径有多少条.

    题解: (k=0)的部分分就是一个最短路计数,如果不会的话可以先去看一下最短路计数.

    我们先考虑不含(0)环的情况.首先我们可以求出从(1)为起点到其他点的最短路的长度,记为(dist[i]).最后对答案产生贡献的状态只有(dist[n]+k),所以我们可以记(f[i][j])表示从(1)出发到(i)节点路径长度为(dist[i]+j)的方案数.

    那么这样我们就可以写出状态转移方程((x->to)):$$f[to][dist[x]+e[i].w-dist[to]+j] += f[x][j]$$

    然后需要特别注意的是枚举顺序,我们在枚举(to)的时候要先将(x)处理完,并且第二维的枚举状态要从小到大枚举.处理好这些,就可以获得(70)分了.

    为什么说是(70)分呢?因为我们这样并不能判断出无穷多条路径的方案.但是我们可以发现,如果存在无穷多条路径,那么在(1)(n)的最短路上(或是在一条(1->n)的路径且长度小于等于(dist[n]+k)上)有一个(0)环,那么就存在无穷多条路径,因为可以在这个(0)环上不断绕.

    所以要判断无穷多条路径,就要先判断是否存在一个(0)环在一条可以对答案产生贡献的路径上.这个可以用拓扑排序实现.

    因为有可能存在(0)环,所以我们更新(dp)数组的时候不能再一边遍历图,一遍更新数组了,这时候我们就需要对要更新的状态排个序,也就是让(dist)小的排在前面更新,但是又由于(0)边的存在,我们需要让一条边的起点来更新终点,所以在多个点(dist)相同的时候,可以按照拓扑序来排.

    细节比较多,可以自己多想想.

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 1e5+5;
    const int M = 2e5+5;
    const int K = 50+5;
    
    int T, n, m, k, yyj, ans = 0, dist[N][2], vis[N][2], point[N], in[N], ord[N];
    int last[N][2], last0[N], ecnt[2], ecnt0 = 0;
    int f[N][K];
    
    struct edge{
        int to, nex, w;
    }e[M][2], e0[M];
    
    struct node{
        int id, dis;
        bool operator < (const node &a) const{
    		return dis > a.dis;
        }
    };
    
    int gi(){
        int res = 0, f = 1; char i = getchar();
        while(i < '0' || i > '9'){ if(i == '-') f = -1; i = getchar(); }
        while(i >= '0' && i <= '9') res = res*10+i-'0', i = getchar();
        return res*f;
    }
    
    void clear(){
        memset(last, 0, sizeof(last)), ans = ecnt[0] = ecnt[1] = 0;
        memset(last0, 0, sizeof(last0)), ecnt0 = 0;
        memset(in, 0, sizeof(in)), memset(vis, 0, sizeof(vis));
        memset(dist, 127/3, sizeof(dist));
        memset(ord, 0, sizeof(ord));
    }
    
    void add(int x, int y, int z, int t){
        e[++ecnt[t]][t].to = y, e[ecnt[t]][t].w = z, e[ecnt[t]][t].nex = last[x][t], last[x][t] = ecnt[t];
    }
    
    void add0(int x, int y){
        e0[++ecnt0].to = y, e0[ecnt0].nex = last0[x], last0[x] = ecnt0;
    }
    
    void dijkstra(int st, int t){
        priority_queue <node> q; q.push((node){ st, 0 });
        dist[st][t] = 0;
        while(!q.empty()){
    		node x = q.top(); q.pop();
    		if(vis[x.id][t]) continue; vis[x.id][t] = 1;
    		for(int to, i = last[x.id][t]; i; i = e[i][t].nex){
    			to = e[i][t].to;
    			if(dist[to][t] > x.dis+e[i][t].w)
    				dist[to][t] = x.dis+e[i][t].w, q.push((node){ to, dist[to][t] });
    		}
        }
    }
    
    bool cmpdis(int a, int b){
        if(dist[a][0] == dist[b][0]) return ord[a] < ord[b];
        return dist[a][0] < dist[b][0];
    }
    
    bool topsort(){
        queue <int> q; int cnt = 0;
        for(int i = 1; i <= n; i++)
    		if(in[i] == 0) q.push(i), ord[i] = ++cnt;
        while(!q.empty()){
    		int x = q.front(); q.pop();
    		for(int to, i = last0[x]; i; i = e0[i].nex){
    			to = e0[i].to, in[to]--;
    			if(in[to] == 0) q.push(to), ord[to] = ++cnt;
    		}
        }
        for(int i = 1; i <= n; i++)
    		if(in[i] && dist[i][0]+dist[i][1] <= dist[n][0]+k) return false;
        return true;
    }
    
    void solve(int st){
        memset(f, 0, sizeof(f)), f[st][0] = 1;
        for(int i = 1; i <= n; i++) point[i] = i;
        sort(point+1, point+n+1, cmpdis);
        for(int l = 0; l <= k; l++){
    		for(int i = 1; i <= n; i++){
    			int x = point[i];
    			for(int j = last[x][0]; j; j = e[j][0].nex){
    				int to = e[j][0].to;
    				if(dist[x][0]+e[j][0].w+l <= dist[to][0]+k)
    					(f[to][dist[x][0]+e[j][0].w+l-dist[to][0]] += f[x][l]) %= yyj;
    			}
    		}
        }
    }
    
    int main(){
        ios::sync_with_stdio(false);
        int x, y, z; T = gi();
        while(T--){
    		clear(); n = gi(), m = gi(), k = gi(), yyj = gi();
    		for(int i = 1; i <= m; i++){
    			x = gi(), y = gi(), z = gi(), add(x, y, z, 0), add(y, x, z, 1);
    			if(z == 0) add0(x, y), in[y]++;
    		}
    		dijkstra(1, 0), dijkstra(n, 1);
    		if(!topsort()){ cout << -1 << endl; continue; }
    		solve(1);
    		for(int i = 0; i <= k; i++) (ans += f[n][i]) %= yyj;
    		cout << ans << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    iOS AutoLayout的用法
    UIPickerView的使用(一)
    UIPickerView的使用(二)
    logging模块
    configparser模块
    hashlib模块
    json & pickle 模块
    对表的操作
    表记录曾删改查
    库、表曾删改查和存储引擎
  • 原文地址:https://www.cnblogs.com/BCOI/p/9669948.html
Copyright © 2011-2022 走看看