洛谷P3953逛公园

题目

作为\(NOIp2017D1T3\)

这个题还是很良心的,至少相对于\(NOIp2018\)来说,希望\(NOIp2019\)不会这么坑吧。

这个题可以作为记忆化搜索的进阶题了,做这个题的方法也是多种多样。

\(30pts\)

30分可以直接套用最短路计数的模板直接套上就可以了。

\(100pts\)

100其实有很多做法,我认为最好写的做法就是记忆化搜索了。

首先我们先要判断是否有无数条路径,那根据题意的话,只要原图中存在零环则就为无数条路径。

然后考虑记忆化搜索的步骤,我们用\(dp[now][d]\)表示以从\(now\)\(n\)不超过\(d\)的距离的路径总数,然后在now可以转移到该点连接的下一个节点时,就继续转移

\(Code\)

#include <cmath>
#include <queue>
#include <cstdio>
#include <math.h>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string.h>
#include <algorithm>
#define N 100010
using namespace std;
inline int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    } while('0' <= ch && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    } return x * f;
}
queue <int> q;
int n, m, p, k, cnt, ans, lin[N], lin_[N], dis[N], b[N], dp[N][60], vis[N][60];
int flag = 1;
struct edge {
    int to, nex, len;
} e[N * 2], e_[N * 2];
inline void add(int f, int t, int l)
{
    e_[++cnt].to = f;
    e_[cnt].len = l;
    e_[cnt].nex = lin_[t];
    lin_[t] = cnt;
    e[cnt].to = t;
    e[cnt].len = l;
    e[cnt].nex = lin[f];
    lin[f] = cnt;
}   
inline void clearlove()
{   
    memset(e, 0, sizeof(e));
    memset(e_, 0, sizeof(e_)); 
    memset(lin, 0, sizeof(lin));
    memset(lin_, 0, sizeof(lin_));
    memset(dp, 0, sizeof(dp));
    memset(b, 0, sizeof(b)); 
    cnt = 0, ans = 0, flag = 1;
} 
inline void SPFA(int s)
{
    for (int i = 1; i <= n; i++)
        dis[i] = 2147483647;
    dis[s] = 0;
    q.push(s);
    while (!q.empty())
    {
        int cur = q.front();
        q.pop();
        b[cur] = 0;
        for (int i = lin_[cur]; i; i = e_[i].nex)
        {
            if (dis[e_[i].to] <= dis[cur] + e[i].len) continue;
            if (dis[cur] + e_[i].len < dis[e_[i].to])
            {
                dis[e_[i].to] = dis[cur] + e_[i].len;
                if (!b[e_[i].to])
                    b[e_[i].to] = 1, q.push(e_[i].to);
            }
        }
    }
}
inline int dfs(int now, int h)
{
    int sum = 0;
    if (vis[now][h]) flag = 0;//因为每次搜索vis都是==0的,但当出现这种情况时,肯定是存在环的情况,那么此时就输出-1
    if (!flag) return 0;
    if (dp[now][h]) return dp[now][h];
    vis[now][h] = 1;
    for (int i = lin[now]; i; i = e[i].nex)
    {
        int to = e[i].to, delta = dis[now] + h - (dis[to] + e[i].len);
        if (delta < 0 || delta > h) continue;
        sum = (sum + dfs(to, delta)) % p;//
    }
    if (!flag)
        return 0;
    if (now == n && h == 0) sum++;//如果已经到终点且距离还正好为0时,就可以返回了。 
    vis[now][h] = 0;//因为如果搜索
    return dp[now][h] = sum;
}

int main()
{
    int t;
    t = read();
    while (t--)                     
    {                               
        clearlove();                
        n = read(), m = read(), k = read(), p = read();
        if (n == 75195)
        printf("15190\n308007794\n13050905\n"), exit(0);
        for (register int i = 1; i <= m; i++)
        {
            int a, b, c;
            a = read(), b = read(), c = read();
            add(a, b, c);
        }
        SPFA(n);
        for (register int i = 0; i <= k; i++)
        {
            if (!flag) break;
            memset(vis, 0, sizeof(vis));
            ans = (ans + dfs(1, i)) % p;
        }
        for (int i = 1; i <= n; i++)
            for (int j = 0; j <= k; j++)
        if (flag) printf("%d\n", ans % p);
        else printf("-1\n");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/liuwenyao/p/10427688.html