• 《P3953 [NOIP2017 提高组] 逛公园》


    这题之前没做出来,现在看懂了。

    因为k很小,所以考虑求出所有恰好满足 dis <= len <= dis + k的路径数。

    这里就是用了枚举的思想,去枚举每种情况。

    然后dp[i][j]表示从1走到i点用了比最短路多j的路径的方案数。

    然后去转移。

    有两种思路,从n开始倒着搜dp,比较容易理解。

    我这里是正着来dp了。

    从u -> v 的w路径,比到v的最短路多了dis[v] + len - dis[u] - val。还剩下这么多路径,如果 < 0就说明不可以走到。

    不小于就可以继续dp转移。

    注意这里的一个问题就是有两种情况的零环。

    1:有零环,且会导致无穷解的出现,那么就在dfs的途中对每种情况都做一个记录,这样第二次走到的时候就说明有零环了。

    2:有零环,但是从1 -> n的路径中零环不会走入,或者就算进入零环,环两边的最小路径也 > dis + k.

    对于第二种情况,我们做两次正着 和 反着的dij是否能走到,是否环两边最小路径 > dis + k。  

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    const int N = 2e5 + 5;
    const int M = 3e5 + 5;
    const LL Mod = 1e9 + 7;
    #define pi acos(-1)
    #define INF 1e9
    #define dbg(ax) cout << "now this num is " << ax << endl;
    namespace FASTIO{
        inline LL read(){
            LL x = 0,f = 1;char c = getchar();
            while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
            while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
            return x*f;
        }
    }
    using namespace FASTIO;
    
    int n,m,k,p,dis1[N],dis2[N],dp[N][55];
    bool tag[N],f[N][55];
    struct Node{int to,dis;};
    vector<Node> G[N],RG[N];
    void dij(int x,int dis[],vector<Node> E[]){
        for(int i = 1;i <= n;++i) dis[i] = INF;
        dis[x] = 0;
        priority_queue<pii,vector<pii>,greater<pii> > Q;
        Q.push(pii{dis[x],x});
        while(!Q.empty()){
            int u = Q.top().second;
            int d = Q.top().first;
            Q.pop();
            if(d > dis[u]) continue;
            for(auto v : E[u]){
                if(dis[v.to] > dis[u] + v.dis){
                    dis[v.to] = dis[u] + v.dis;
                    Q.push(pii{dis[v.to],v.to});
                }
            }
        }
    }
    LL dfs(int u,int len){
        //printf("u is %d len is %d
    ",u,len);
        if(f[u][len] != 0) return -1;
        if(dp[u][len] != -1) return dp[u][len];
        f[u][len] = 1;
        LL sum = 0;
        if(u == n && len == 0) sum++;
        for(auto v : G[u]){
            if(tag[v.to] == 0) continue;
            int x =    len + dis1[v.to] - dis1[u] - v.dis;
            if(x < 0) continue;
            int tmp = dfs(v.to,x);
            if(tmp == -1) return -1;
            sum = (sum + tmp) % p;
        }
        f[u][len] = 0;
        return dp[u][len] = sum;
    }
    int main()
    {
        int ca;ca = read();
        while(ca--){
            n = read(),m = read(),k = read(),p = read();
            for(int i = 1;i <= n;++i) G[i].clear(),RG[i].clear(),tag[i] = 0;
            memset(dp,-1,sizeof(dp));
            memset(f,0,sizeof(f));
            while(m--){
                int a,b,c;a = read(),b = read(),c = read();
                G[a].push_back(Node{b,c});
                RG[b].push_back(Node{a,c});
            }
            dij(1,dis1,G);
            dij(n,dis2,RG);
            for(int i = 1;i <= n;++i){
                if(dis1[i] == INF || dis2[i] == INF) continue;
                if(dis1[i] + dis2[i] > k + dis1[n]) continue;
                tag[i] = 1;
            }
            LL ans = 0;
            bool flag = false;
            for(int kk = 0;kk <= k;++kk){
                LL sum = dfs(1,kk);
                if(sum == -1) flag = true;
                else ans = (ans + sum) % p;
            //    printf("sum is %lld flag is %d
    ",sum,flag);
            }
            if(flag) printf("-1
    ");
            else printf("%lld
    ",ans);
        }    
        system("pause");
        return 0;
    }
    View Code
  • 相关阅读:
    c#泛型的使用
    如何调试由于heap corruption导致的程序崩溃的简单示例
    Windows的SEH机理简要介绍
    利用定制行为扩展WCF之利用MessageInsepctor behaviourExtension扩展WCF行为(自定义消息头)
    欧拉函数
    JZOJ.1349 最小公约数
    关于扩展中国剩余定理(excrt)的证明与拙见
    【USACO 2021 US Open, Gold】United Cows of Farmer John & JZOJ7220
    线性求逆元
    【USACO 2021 January Contest, Platinum】Problem 1. Sum of Distances JZOJ.7241
  • 原文地址:https://www.cnblogs.com/zwjzwj/p/14326221.html
Copyright © 2020-2023  润新知