NOIP2017 逛公园
题目描述
策策同学特别喜欢逛公园。公园可以看成一张NN个点MM条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,NN号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
策策每天都会去逛公园,他总是从1号点进去,从NN号点出来。
策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到NN号点的最短路长为dd,那么策策只会喜欢长度不超过d + Kd+K的路线。
策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
为避免输出过大,答案对PP取模。
如果有无穷多条合法的路线,请输出−1。
输入输出格式
输入格式:
第一行包含一个整数 TT, 代表数据组数。
接下来TT组数据,对于每组数据: 第一行包含四个整数 N,M,K,PN,M,K,P,每两个整数之间用一个空格隔开。
接下来MM行,每行三个整数a_i,b_i,c_iai,bi,ci,代表编号为a_i,b_iai,bi的点之间有一条权值为 c_ici的有向边,每两个整数之间用一个空格隔开。
输出格式:
输出文件包含 TT 行,每行一个整数代表答案。
输入输出样例
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
3 -1
说明
【样例解释1】
对于第一组数据,最短路为 3。 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 为 3 条合法路径。
【测试数据与约定】
对于不同的测试点,我们约定各种参数的规模不会超过如下
测试点编号 | TT | NN | MM | KK | 是否有0边 |
---|---|---|---|---|---|
1 | 5 | 5 | 10 | 0 | 否 |
2 | 5 | 1000 | 2000 | 0 | 否 |
3 | 5 | 1000 | 2000 | 50 | 否 |
4 | 5 | 1000 | 2000 | 50 | 否 |
5 | 5 | 1000 | 2000 | 50 | 否 |
6 | 5 | 1000 | 2000 | 50 | 是 |
7 | 5 | 100000 | 200000 | 0 | 否 |
8 | 3 | 100000 | 200000 | 50 | 否 |
9 | 3 | 100000 | 200000 | 50 | 是 |
10 | 3 | 100000 | 200000 | 50 | 是 |
对于 100%的数据, 1 \le P \le 10^9,1 \le a_i,b_i \le N ,0 \le c_i \le 10001≤P≤109,1≤ai,bi≤N,0≤ci≤1000。
数据保证:至少存在一条合法的路线。
首先dp很好想:f[i][j]表示到i,比最短路大j的方案数
转移可以用记忆化广搜,但效率不高
实际上转移顺序的问题可以通过拓扑排序实现
对于每一个j值(从小到大枚举),都可以用最短路径的拓扑序先对j进行转移
f[Top[]][j]+=f[i][j]
然后就可以枚举每一个点的边进行转移,因为与最短路差肯定大于j,所以不用考虑顺序:f[v][j+w+dist[u]-dist[v]]+=f[u][j]
判断无解因为是按照最短路径拓扑,所以如果有节点未出队且通过该点的最短
路<=(1,n)的最短路+k
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 struct Node 8 { 9 int next,to,dis; 10 }edge[500011]; 11 int head[2][200011],dist[2][200011],n,m,k,inf=2e8; 12 int f[200011][55],ans,Mod,num,sum,T[500011],du[200011]; 13 bool vis[200011]; 14 void add(int x,int u,int v,int dis) 15 { 16 num++; 17 edge[num].next=head[x][u]; 18 head[x][u]=num; 19 edge[num].to=v; 20 edge[num].dis=dis; 21 } 22 queue<int>Q; 23 void SPFA(int t) 24 {int i; 25 for (i=1;i<=n;i++) 26 dist[t][i]=inf,vis[i]=0; 27 while (Q.empty()==0) Q.pop(); 28 if (t==0) Q.push(1),dist[t][1]=0; 29 else Q.push(n),dist[t][n]=0; 30 while (Q.empty()==0) 31 { 32 int u=Q.front(); 33 vis[u]=0; 34 Q.pop(); 35 for (i=head[t][u];i;i=edge[i].next) 36 { 37 int v=edge[i].to; 38 if (dist[t][u]+edge[i].dis<dist[t][v]) 39 { 40 dist[t][v]=dist[t][u]+edge[i].dis; 41 if (vis[v]==0) vis[v]=1,Q.push(v); 42 } 43 } 44 } 45 } 46 void Top() 47 {int i,j; 48 while (Q.empty()==0) Q.pop(); 49 for (i=0;i<=n;i++)du[i]=0; 50 for (i=1;i<=n;i++) 51 { 52 for (j=head[0][i];j;j=edge[j].next) 53 if (dist[0][i]+edge[j].dis==dist[0][edge[j].to]) du[edge[j].to]++; 54 } 55 for (i=1;i<=n;i++) 56 if (du[i]==0) Q.push(i),sum++,T[sum]=i; 57 while (Q.empty()==0) 58 { 59 int u=Q.front(); 60 Q.pop(); 61 for (i=head[0][u];i;i=edge[i].next) 62 { 63 int v=edge[i].to; 64 if (dist[0][v]==dist[0][u]+edge[i].dis) 65 { 66 du[v]--; 67 if (du[v]==0) Q.push(v),sum++,T[sum]=v; 68 } 69 } 70 } 71 } 72 void DP() 73 {int i,j,l; 74 f[1][0]=1; 75 for (i=0;i<=k;i++) 76 { 77 for (j=1;j<=sum;j++) 78 { 79 int u=T[j]; 80 if (f[u][i]==0) continue; 81 for (l=head[0][u];l;l=edge[l].next) 82 { 83 int v=edge[l].to; 84 if (dist[0][v]==dist[0][u]+edge[l].dis) 85 f[v][i]=(f[v][i]+f[u][i])%Mod; 86 } 87 } 88 for (j=1;j<=n;j++) 89 if (f[j][i]) 90 for (l=head[0][j];l;l=edge[l].next) 91 { 92 int v=edge[l].to; 93 if (dist[0][v]!=dist[0][j]+edge[l].dis) 94 if (dist[0][j]+edge[l].dis-dist[0][v]+i<=k) 95 { 96 f[v][dist[0][j]+edge[l].dis-dist[0][v]+i]=(f[v][dist[0][j]+edge[l].dis-dist[0][v]+i]+f[j][i])%Mod; 97 } 98 } 99 } 100 ans=0; 101 for (i=0;i<=k;i++) 102 ans=(ans+f[n][i])%Mod; 103 printf("%d\n",ans); 104 } 105 int main() 106 {int T,i,j,u,v,d,flag; 107 cin>>T; 108 while (T--) 109 { 110 cin>>n>>m>>k>>Mod; 111 num=0; 112 sum=0; 113 for (i=0;i<=200000;i++) 114 head[0][i]=head[1][i]=0; 115 for (i=0;i<=n;i++) 116 for (j=0;j<=k;j++) 117 f[i][j]=0; 118 for (i=1;i<=m;i++) 119 { 120 scanf("%d%d%d",&u,&v,&d); 121 add(0,u,v,d);add(1,v,u,d); 122 } 123 SPFA(0);SPFA(1); 124 Top(); 125 flag=0; 126 for (i=1;i<=n;i++) 127 if (du[i]&&dist[0][i]+dist[1][i]<=dist[0][n]+k) 128 { 129 printf("-1\n"); 130 flag=1; 131 break; 132 } 133 if (flag==0) 134 DP(); 135 } 136 }