[Noip2017]逛公园

其实真的是一道思博题啊。为啥考试我就没想出来??

因为长得太拓扑了(其他正解好像也确实是拓扑),然后顾忌到环就没想。太菜了还是。

后来上课闲的没事一想,靠,记忆化搜索啊。这样一定是所有父亲都计算完不重不漏。

(正着想不也是嘛)

很难受,要不然能多点分。


 

【问题描述】

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

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

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

策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗?

为避免输出过大,答案对$P$取模。

如果有无穷多条合法的路线,请输出$−1$。

【输入格式】

第一行包含一个整数$T$, 代表数据组数。

接下来$T$组数据,对于每组数据:

第一行包含四个整数 $N,M,K,P$, 每两个整数之间用一个空格隔开。

接下来$M$行,每行三个整数$a_i,b_i,c_i$, 代表编号为$a_i,b_i$的点之间有一条权值为$c_i$的有向边,每两个整数之间用一个空格隔开。

【输出格式】

输出文件包含$T$行,每行一个整数代表答案。

对于第一组数据,最短路为 3。

1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3 条合法路径。


 

用$dp[u][k]$表示从$1$到$u$点的所有路径里,比最短路多$k$的路径数。

则有

$dp[u][k]=\sum dp[v][dis[u]+k-w-dis[v]]$

$dis[x]$是从$1$到$x$的最短路。spfa预处理复杂度$O(n \times ??? )$。

$w$是边权。$v$是所有有指向$u$的边的点。

记忆化搜索即可。判0环的话,还没有写出完美的写法,如果有不包含$1$的0环,那么一个未确定的$dp$值被访问两次说明存在。

复杂度$O(nk)$。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
using namespace std;
const int N=100010;
const int inf=0x3f3f3f3f;
typedef long long ll;
inline int read(){
    int r=0,f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){r=r*10+c-'0';c=getchar();}
    return r*f;
}
struct Edge{
    int to,nxt,w;
}e[N*2],E[N*2];
int head[N],Head[N],cnt=1;
void add(int u,int v,int w){
    e[cnt]=(Edge){v,head[u],w};
    head[u]=cnt;
    E[cnt]=(Edge){u,Head[v],w};
    Head[v]=cnt++;
}
int n,m,K,P;
bool inq[N];
int dis[N],f[N][51],c[N][51];
void spfa(){
    deque<int>q;
    memset(dis,63,sizeof dis);
    dis[1]=0;q.push_back(1);inq[1]=1;
    while(!q.empty()){
        int u=q.front();q.pop_front();inq[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to,w=e[i].w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!inq[v]){
                    if(!q.empty()&&dis[v]<=dis[q.front()])
                    q.push_front(v);
                    else q.push_back(v);
                }
            }
        }
    }
}bool ff=0;
void Clear(){
    n=m=K=P=0;
    memset(inq,0,sizeof inq);
    memset(f,-1,sizeof f);
    memset(head,0,sizeof head);
    memset(Head,0,sizeof Head);
    memset(c,0,sizeof c);
    memset(e,0,sizeof e);
    memset(E,0,sizeof E);cnt=1;
    ff=0;
}
void Init(){
    n=read(),m=read(),K=read(),P=read();
    while(m--){
        int u=read(),v=read(),w=read();
        add(u,v,w);
    }
}
 
int dfs(int u,int k){
    if(~f[u][k])return f[u][k];
    c[u][k]=1;
    f[u][k]=0;
    for(int i=Head[u];i;i=E[i].nxt){
        int v=E[i].to,w=E[i].w;
        int t=dis[u]+k-w-dis[v];
        if(t<0)continue;
        if(c[v][t])ff=1;
        f[u][k]+=dfs(v,t),f[u][k]%=P;
    }
    c[u][k]=0;
    return f[u][k];
}
void Solve(){
    spfa();
    f[1][0]=1;
    int ans=0;
    for(int j=0;j<=K;j++)
    ans+=dfs(n,j),ans%=P;
    dfs(n,K+1);
    if(ff){
        cout<<-1<<endl;
        return ;
    }
    printf("%d\n",ans);
}
int main(){
    int T=read();
    while(T--){
        Clear();
        Init();
        Solve();
    }
    return 0;
}
View Code
posted @ 2017-11-18 20:30  orzzz  阅读(2088)  评论(0编辑  收藏  举报