洛谷3953:逛公园——题解

https://www.luogu.org/problemnew/show/P3953

策策同学特别喜欢逛公园。公园可以看成一张n个点m条边构成的有向图,且没有自环和重边。其中1号点是公园的入口,n号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。

策策每天都会去逛公园,他总是从1号点进去,从n号点出来。

策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果1号点到n号点的最短路长为d,那么策策只会喜欢长度不超过d + k的路线。

策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗? 为避免输出过大,答案对p取模。 如果有无穷多条合法的路线,请输出−1。

相信自己多做点dp题就能把思维提上来的我是不是很sb……

参考洛谷第一篇题解。

没记错当时奔着70pts去的结果30pts。

30pts很简单就不讲了。

70pts设f[i][j]为1~i路径长度比最短距离<=j的个数。

然后dij对dis处理之后对dis排序再进行更新。//我就是错在这个地方。

100pts其实0边也很好处理,拓扑找即可。

但是放在考场上会被卡常。

所以我们选择记忆化搜索。

但是如果普通的搜的话就又会犯我上面的错误。

思考错误的产生原因:即我们的偏移量k可能会出问题因为有些点有可能到不了n。

所以我们先dij反图处理点到n的距离,再在正图跑即可。

至于0环的判断,只要同一状态被dfs时搜过两次就一定是进了0环。

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=100010;
const int M=200010;
const int K=51;
const int INF=1e9;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct node{
    int w,to,nxt;
}e[2*M];
int n,m,k,p,cnt,head[N],dis[N],f[N][K];
bool vis[N],in[N][K];
struct cmp{
    bool operator ()(int a,int b){
        return dis[a]>dis[b];
    }
};
priority_queue<int,vector<int>,cmp>q;
inline void add(int u,int v,int w){
    e[++cnt].to=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt;
}
void bfs(int s){
    dis[s]=0;vis[s]=1;q.push(s);
    while(!q.empty()){
        int u=q.top();q.pop();vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
            if(i&1)continue;
            int v=e[i].to,w=e[i].w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!vis[v]){
                    vis[v]=1;q.push(v);
                }
            }
        }
    }
}
int dp(int u,int t){
    if(in[u][t])return f[u][t]=-1;
    if(f[u][t])return f[u][t];
    if(u==n)f[u][t]=1;
    in[u][t]=1;
    for(int i=head[u];i;i=e[i].nxt){
        if(!(i&1))continue;
        int v=e[i].to,w=e[i].w;
        int tmp=dis[v]+w-dis[u];
        if(tmp<=t){
            int tot=dp(v,t-tmp);
            if(tot==-1)return f[u][t]=-1;
            f[u][t]=(f[u][t]+tot)%p;
        }
    }
    in[u][t]=0;
    return f[u][t];
}
inline void init(){
    cnt=0;
    for(int i=1;i<=n;i++){
        dis[i]=INF,vis[i]=head[i]=0;
        for(int j=0;j<=k;j++)f[i][j]=in[i][j]=0;
    }
}
int main(){
    int t=read();
    while(t--){
        n=read(),m=read(),k=read(),p=read();
        init();
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),w=read();
            add(u,v,w);add(v,u,w);
        }
        bfs(n);
        printf("%d\n",dp(1,k));
    }
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-04-15 16:33  luyouqi233  阅读(432)  评论(0编辑  收藏  举报