【Noip2017 逛公园】
题目描述
策策同学特别喜欢逛公园。公园可以看成一张N个点M条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从N号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到N号点的最短路长为d,那么策策只会喜欢长度不超过d+K的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对P取模。
如果有无穷多条合法的路线,请输出−1。
输入格式:
第一行包含一个整数 T, 代表数据组数。
接下来T组数据,对于每组数据: 第一行包含四个整数 N,M,K,P,每两个整数之间用一个空格隔开。
接下来M行,每行三个整数ai,bi,ci,代表编号为ai,bi的点之间有一条权值为ci的有向边,每两个整数之间用一个空格隔开。
输出格式:
输出文件包含 T 行,每行一个整数代表答案。
输出样例:
3 -1
题解:
首先可以想到dp,f[k][u]表示到达u这个点时距离为dis[u]+k的路径总数(否则空间存不下,并且发现K<=50),然后转移,f[k+dis[u]+w-dis[v]][v]+=f[k][u]。
然后会发现转移顺序在边权为0或者在最短路上会出错。这时转移是f[k][v]+=f[k][u],f[k][u]必须要先被更新。
因此要把拓扑下求出更新顺序即可。(比如x->y->z三条边权为0或者三个点都在最短路上,更新顺序z,y,x)
判断无解的话就是有一个点在0环中且经过这个点的路路径长度满足<=dis[n]+k。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<queue> 6 using namespace std; 7 struct node{ 8 int to,next,v; 9 } e[200010],g[200010]; 10 int cnt,tot; 11 int head[100010],last[100010]; 12 int vis[100010],n,m,K,P,dis1[100010],dis2[100010],in[100010]; 13 int f[55][100010]; 14 priority_queue<pair<int,int> > q; 15 int s[100010]; 16 inline void insert(int u,int v,int w){ 17 e[++cnt].next=head[u]; 18 head[u]=cnt; 19 e[cnt].to=v; 20 e[cnt].v=w; 21 } 22 inline void add(int u,int v,int w){ 23 g[++tot].next=last[u]; 24 last[u]=tot; 25 g[tot].to=v; 26 g[tot].v=w; 27 } 28 inline void dj1(){ 29 memset(vis,0,sizeof(vis)); 30 for(int i=1;i<=n;i++) dis1[i]=1e9+7; 31 dis1[1]=0; 32 q.push(make_pair(0,1)); 33 while(!q.empty()){ 34 int now=q.top().second;q.pop(); 35 if(vis[now]) continue; 36 vis[now]=1; 37 for(int i=head[now];i;i=e[i].next){ 38 if(dis1[now]+e[i].v<dis1[e[i].to]){ 39 dis1[e[i].to]=dis1[now]+e[i].v; 40 q.push(make_pair(-dis1[e[i].to],e[i].to)); 41 } 42 } 43 } 44 } 45 inline void dj2(){ 46 memset(vis,0,sizeof(vis)); 47 for(int i=1;i<=n;i++) dis2[i]=1e9+7; 48 dis2[n]=0; 49 q.push(make_pair(0,n)); 50 while(!q.empty()){ 51 int now=q.top().second;q.pop(); 52 if(vis[now]) continue; 53 vis[now]=1; 54 for(int i=last[now];i;i=g[i].next){ 55 if(dis2[now]+g[i].v<dis2[g[i].to]){ 56 dis2[g[i].to]=dis2[now]+g[i].v; 57 q.push(make_pair(-dis2[g[i].to],g[i].to)); 58 } 59 } 60 } 61 } 62 inline void dp(){ 63 int ans=0; 64 memset(f,0,sizeof(f)); 65 f[0][1]=1; 66 for(int k=0;k<=K;k++){ 67 for(int i=1;i<=s[0];i++){ 68 for(int now=s[i],j=head[now];j;j=e[j].next) 69 if(k+dis1[now]+e[j].v-dis1[e[j].to]<=K) 70 f[k+dis1[now]+e[j].v-dis1[e[j].to]][e[j].to]=(f[k][now]+f[k+dis1[now]+e[j].v-dis1[e[j].to]][e[j].to])%P; 71 } 72 } 73 for(int i=0;i<=K;i++) 74 ans=(ans+f[i][n])%P; 75 printf("%d\n",ans); 76 } 77 inline void solve(){ 78 queue<int> p; 79 scanf("%d%d%d%d",&n,&m,&K,&P); 80 memset(head,0,sizeof(head)); 81 memset(last,0,sizeof(last)); 82 memset(s,0,sizeof(s)); 83 memset(in,0,sizeof(in)); 84 cnt=tot=0; 85 int u,v,w; 86 for(int i=1;i<=m;i++){ 87 scanf("%d%d%d",&u,&v,&w); 88 insert(u,v,w); 89 add(v,u,w); 90 } 91 dj1();dj2(); 92 for(int i=1;i<=n;i++){ 93 for(int j=head[i];j;j=e[j].next){ 94 if(dis1[i]+e[j].v==dis1[e[j].to]) in[e[j].to]++; 95 } 96 } 97 for(int i=1;i<=n;i++){ 98 if(!in[i]){ 99 s[++s[0]]=i; 100 p.push(i); 101 } 102 } 103 while(!p.empty()){ 104 int now=p.front();p.pop(); 105 for(int j=head[now];j;j=e[j].next){ 106 if(dis1[now]+e[j].v==dis1[e[j].to]){ 107 in[e[j].to]--; 108 if(!in[e[j].to]){ 109 p.push(e[j].to); 110 s[++s[0]]=e[j].to; 111 } 112 } 113 } 114 } 115 for(int i=1;i<=n;i++){ 116 if(in[i] && dis1[i]+dis2[i]<=dis1[n]+K){ 117 printf("-1\n"); 118 return; 119 } 120 } 121 dp(); 122 } 123 int main(){ 124 int T; 125 scanf("%d",&T); 126 while(T--){ 127 solve(); 128 } 129 return 0; 130 }