NOIP2017 逛公园

传送门

这个题听说是NOIP2017最难的题?反正我当时啥也不会爆零……

首先我们看送的分,直接跑最短路计数有30pts。

之后就不会了hhhhh……

我们仔细观察题目之后发现,这个题k的数据范围特别小,只有50,也就是说我们最多只会统计比最短路径长50的所有路径条数,那我们其实完全是可以这样DP的。

具体操作如下,我们首先建出正图,在正图上跑一遍dij,记录起点到每个点的最短距离。之后再建出反图,处理出有哪些点是能到达终点的,这样到不了终点的点就可以被忽略。

这样预处理之后,我们直接上大招:记忆化搜索!!!(这东西真的不是一般强力)

我们传两个参,一个是当前的节点,另一个是当前剩余的距离(因为我们最多允许走比最短路长k的距离,所以我们可以在记忆化搜索的时候把它作为参数传进去)。这样的话记忆化搜索就变得很简单。首先如果这个点无法到达终点,直接返回即可,如果这个点已经存过值(dp数组已经被更新过),直接返回这个值就可以(记忆化)。否则的话我们继续进行深搜,减去当前所多花费的路程继续向下DFS即可。

最后一个问题就是怎么判断0环。(因为如果出现无穷多条最短路的话一定是有0环出现)我们在记忆化搜索的时候使用vis数组记录当前是否被走过,在这个点结束dfs的时候将其还原。这样的话如果有0环,它会在vis被还原之前就走到这个点,那么就出现0环了。

这样的话我们就做完了……不过这题卡常很厉害……我也不会用线段树求最短路,最后还是开O2勉强卡过去了。

看一下代码。

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<vector>
#include<set>
#define pr pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define sc second
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
using namespace std;
typedef long long ll;
const int M = 100005;
const int N = 10000005;
const int INF = 2147483645;
 
int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >='0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

int n,m,t,k,p,a,b,c,dis[M],dp[M][55],head[M],head1[M],ecnt,ecnt1;
bool alive[M],vis[M][55];

struct edge
{
    int next,to,v;
}e[M<<1],e1[M<<1];

set <pr> q;
set <pr> :: iterator it;

void clear()
{
    memset(e,0,sizeof(e));
    memset(e1,0,sizeof(e1));
    memset(head,0,sizeof(head));
    memset(head1,0,sizeof(head1));
    ecnt = ecnt1 = 0;
    rep(i,1,n)
    {
          alive[i] = 0,dis[i] = INF;
    rep(l,0,k) dp[i][l] = -1,vis[i][l] = 0;
    }
}

void add(int x,int y,int z)
{
    e[++ecnt].to = y;
    e[ecnt].v = z;
    e[ecnt].next = head[x];
    head[x] = ecnt;
}

void add1(int x,int y,int z)
{
    e1[++ecnt1].to = y;
    e1[ecnt1].v = z;
    e1[ecnt1].next = head1[x];
    head1[x] = ecnt1;
}

void dij()
{
    int cnt = 0;
    dis[1] = 0;
    q.insert(mp(dis[1],1));
    while(!q.empty())
    {
    pr k = *(q.begin()); q.erase(q.begin());
    for(int i = head[k.sc];i;i = e[i].next)
    {
        if(dis[e[i].to] > dis[k.sc] + e[i].v)
        {
        it = q.find(mp(dis[e[i].to],e[i].to));
        if(it != q.end()) q.erase(it);
        dis[e[i].to] = dis[k.sc] + e[i].v;
        q.insert(mp(dis[e[i].to],e[i].to));
        }
    }
    }
}

void bfs()
{
    queue <int> qu;
    qu.push(n);alive[n] = 1;
    while(!qu.empty())
    {
    int k = qu.front();qu.pop();
    for(int i = head1[k];i;i = e1[i].next)
    {
        int g = e1[i].to;
        if(!alive[g]) alive[g] = 1,qu.push(g);
    }
    }
}

int dfs(int a,int b)
{
    if(b < 0) return 0;
    if(vis[a][b]) return -INF;
    if(dp[a][b] != -1) return dp[a][b];
    vis[a][b] = 1;
    int cur = 0;
    if(a == n) cur++;
    for(int i = head[a];i;i = e[i].next)
    {
    int di = dis[e[i].to] - dis[a];
    if(!alive[e[i].to]) continue;
    int w = dfs(e[i].to,b - (e[i].v - di));
    if(w == -INF) return -INF;
    else
    {
        cur += w;
        if(cur > p) cur -= p;
    }
    }
    dp[a][b] = cur % p;
    vis[a][b] = 0;
    return cur;
}

int main()
{
    t = read();
    while(t--)
    {
    n = read(),m = read(),k = read(),p = read();
    clear();
    rep(i,1,m)
    {
        a = read(),b = read(),c = read();
        add(a,b,c),add1(b,a,c);
    }
    dij();//rep(i,1,n) printf("%d ",dis[i]);enter;
    bfs();//rep(i,1,n) printf("%d ",alive[i]);enter;
    int ans = dfs(1,k);
    if(ans == -INF) printf("-1\n");
    else printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/captain1/p/9716419.html