《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\n",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\n",sum,flag);
        }
        if(flag) printf("-1\n");
        else printf("%lld\n",ans);
    }    
    system("pause");
    return 0;
}
View Code

 

posted @ 2021-01-25 17:08  levill  阅读(135)  评论(0编辑  收藏  举报