本文主要是介绍【NOIP2017提高组正式赛】Day1T3逛公园,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Description
策策同学特别喜欢逛公园。公园可以看成一张o个点n条边构成的有向图,且没有自环和重边。其中1号点是公园的入口,o号点是公园的出口,每条边有一个非负权值,代表策策经过这条边所要花的时间。策策每天都会去逛公园,他总是从1号点进去,从o号点出来。策策喜欢新鲜的事物,他不希望有两天逛公园的路线完全一样,同时策策还是一个特别热爱学习的好孩子,他不希望每天在逛公园这件事上花费太多的时间。如果1号点到o号点的最短路长为e,那么策策只会喜欢长度不超过e + k的路线。策策同学想知道总共有多少条满足条件的路线,你能帮帮他吗?为避免输出过大,答案对p取模。如果有无穷多条合法的路线,请输出−1。
Input
输入文件名为 park.in 。
第一行包含一个整数 T, 代表数据组数。
接下来T组数据,对于每组数据:
第一行包含四个整数 O,N,K,P,每两个整数之间用一个空格隔开。
接下来N行,每行三个整数, 代表编号为的点之间有一条权值为 的有向边,每两个整数之间用一个空格隔开。
Output
输出文件包含 T行,每行一个整数代表答案。
Sample Input
2
5 7 2 10
1 2 1
2 4 0
4 5 2
2 3 2
3 4 1
3 5 2
1 5 3
2 2 0 10
1 2 0
2 1 0
Sample Output
3
-1
样例说明:
对于第一组数据,最短路为 3。
1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3 条合法路径。
数据范围
题解
求最短路是很简单的,
如果只求最短路的路径数也是很简单的,
设 fi 表示从点1到点i的最短路路径条数,
转移的时候就判断当前边是不是在最短路上面,是就转移。
现在要求比最短路大k的,
很自然地想到设多一维,
用 fj,i 表示比当前最短路大j,到达点i的路径数。
关于转移,就有所不同了,
要用拓扑序,而且要先枚举比最短路大多少,
对于有无穷解的情况,就是存在一个权值为0的环在最短路上面。
code
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#define ll long long
#define N 100003
#define inf 1125899906842624
using namespace std;char ch;
void read(int& n)
{n=0;for(ch=getchar();ch<'0' || ch>'9';ch=getchar());for(;'0'<=ch && ch<='9';n=(n<<3)+(n<<1)+ch-48,ch=getchar());
}int nxt[N*2],to[N*2],last[N*2],tot;
int nxt1[N*2],to1[N*2],last1[N*2],tot1;
int nxt2[N*2],to2[N*2],last2[N*2],tot2,g[N];
ll v[N*2],v1[N*2],dis[N],dis1[N];
int T,n,m,k,p,x,y,z;
int d[N*200],head,tail;
ll f[53][N],ans;
bool bz[N],check;void ins(int x,int y,int z)
{nxt[++tot]=last[x];to[tot]=y;v[tot]=z;last[x]=tot;
}void ins1(int x,int y,int z)
{nxt1[++tot1]=last1[x];to1[tot1]=y;v1[tot1]=z;last1[x]=tot1;
}void ins2(int x,int y)
{nxt2[++tot2]=last2[x];to2[tot2]=y;last2[x]=tot2;g[y]++;
}void spfa()
{memset(bz,1,sizeof(bz));memset(dis,127,sizeof(dis));dis[tail=1]=bz[d[1]=1]=head=0;while(head<tail){x=d[++head];for(int i=last[x];i;i=nxt[i])if(dis[x]+v[i]<dis[to[i]]){dis[to[i]]=dis[x]+v[i];if(bz[to[i]]){bz[to[i]]=0;d[++tail]=to[i];}}bz[x]=1;}
}void spfa1()
{memset(bz,1,sizeof(bz));memset(dis1,127,sizeof(dis1));dis1[d[tail=1]=n]=bz[n]=head=0;while(head<tail){x=d[++head];for(int i=last1[x];i;i=nxt1[i])if(dis1[x]+v1[i]<dis1[to1[i]]){dis1[to1[i]]=dis1[x]+v1[i];if(bz[to1[i]]){bz[to1[i]]=0;d[++tail]=to1[i];}}bz[x]=1;}
}void pre()
{head=tail=0;for(int i=1;i<=n;i++)if(g[i]==0)d[++tail]=i;while(head<tail){x=d[++head];for(int i=last2[x];i;i=nxt2[i])if(--g[to2[i]]==0)d[++tail]=to2[i];}
}void add(ll& x,ll y)
{x=y+x>p?y+x-p:y+x;
}int main()
{freopen("park.in","r",stdin);freopen("park.out","w",stdout);read(T);while(T--){read(n);read(m);read(k);read(p);memset(last,0,sizeof(last)); memset(last1,0,sizeof(last1)); memset(last2,0,sizeof(last2)); memset(g,0,sizeof(g));ans=tot=tot1=tot2=0;for(int i=1;i<=m;i++)read(x),read(y),read(z),ins(x,y,z),ins1(y,x,z);spfa();spfa1();for(int i=1;i<=n;i++)for(int j=last[i];j;j=nxt[j])if(dis[i]+v[j]==dis[to[j]])ins2(i,to[j]);pre();check=0;for(int i=1;i<=n;i++)if(g[i] && dis[i]+dis1[i]<=dis[n]+k){check=1;break;}if(check){printf("-1\n");continue;}memset(f,0,sizeof(f));f[0][1]=1;for(int i=0;i<=k;i++)for(int j=1;j<=tail;j++){x=d[j];if(dis[x]>inf || dis1[x]>inf)continue;if(dis[x]+dis1[x]+i>dis[n]+k)continue;for(int p=last[x];p;p=nxt[p]){ll t=dis[x]+v[p]+i-dis[to[p]];if(t<=k)add(f[t][to[p]],f[i][x]);}}ans=0;for(int i=0;i<=k;i++)add(ans,f[i][n]);printf("%lld\n",ans);}return 0;
}
这篇关于【NOIP2017提高组正式赛】Day1T3逛公园的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!