题目描述
策策同学特别喜欢逛公园。公园可以看成一张 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 行,每行一个整数代表答案。
输入输出样例
说明
【样例解释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 。
数据保证:至少存在一条合法的路线。
题解:
PS:百度上第一页的题解都是错的!错的!错的!!!(虽说能过)
太神奇了!最后累加看的我有点迷。。需要好好理解
代码(洛谷会RE3个点的):
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=200005,inf=2e8;
int f[2][N],mod,n,m,K,head[N],nxt[N<<1],to[N<<1],dis[N<<1],num=0;
bool vis[N],imp[N];int Head[N];
void link(int x,int y,int z){
nxt[++num]=head[x];to[num]=y;dis[num]=z;head[x]=num;}
void Link(int x,int y,int z){
nxt[++num]=Head[x];to[num]=y;dis[num]=z;Head[x]=num;}
queue<int>q;
void priwork(bool t){
for(int i=1;i<=n;i++)vis[i]=0,f[t][i]=inf;
if(t==0)q.push(1),vis[1]=1,f[t][1]=0;
else q.push(n),vis[n]=1,f[t][n]=0;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=(t?Head[x]:head[x]);i;i=nxt[i]){
RG int u=to[i];
if(f[t][x]+dis[i]<f[t][u]){
f[t][u]=f[t][x]+dis[i];
if(!vis[u])vis[u]=1,q.push(u);
}
}
vis[x]=0;
}
}
int dp[N][55],d[N],sum=0,Q[N];
void solve(){
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=nxt[j])
if(f[0][i]+dis[j]==f[0][to[j]])d[to[j]]++;
for(int i=1;i<=n;i++)if(!d[i])Q[++sum]=i;
RG int t=0;int x,u;
while(t!=sum){
x=Q[++t];
for(int i=head[x];i;i=nxt[i]){
u=to[i];
if(f[0][x]+dis[i]==f[0][u]){
d[u]--;
if(!d[u])Q[++sum]=u;
}
}
}
}
void Clear(){
memset(dp,0,sizeof(dp));
for(RG int i=0;i<N;i++)Q[i]=d[i]=head[i]=Head[i]=imp[i]=0;
sum=0;num=0;
}
inline void add(RG int &x,int y){x+=y;if(x>=mod)x-=mod;}
void work()
{
Clear();
int x,y,z;
scanf("%d%d%d%d",&n,&m,&K,&mod);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
link(x,y,z);Link(y,x,z);
}
priwork(0);priwork(1);solve();
for(int i=1;i<=n;i++)
if(d[i]>0 && f[0][i]+f[1][i]<=f[0][n]+K){
puts("-1");return ;
}
dp[1][0]=1;
for(int k=0;k<=K;k++){
for(int P=1;P<=sum;P++){
int i=Q[P];
if(!dp[i][k])continue;
for(RG int j=head[i];j;j=nxt[j]){
x=to[j];
if(f[0][i]+dis[j]==f[0][x])
add(dp[x][k],dp[i][k]);
}
}
for(RG int i=1;i<=n;i++){
if(!dp[i][k])continue;
for(RG int j=head[i];j;j=nxt[j]){
x=to[j];
if(f[0][i]+dis[j]!=f[0][x]
&& f[0][i]+k+dis[j]-f[0][x]<=K)
add(dp[x][f[0][i]+k+dis[j]-f[0][x]],dp[i][k]);
}
}
}
int ans=0;
for(int i=0;i<=K;i++)add(ans,dp[n][i]);
printf("%d\n",ans);
}
int main()
{
freopen("park.in","r",stdin);
freopen("park.out","w",stdout);
int T;cin>>T;
while(T--)work();
return 0;
}
2018.05.16:我傻逼,只要判断当前点到终点的最短距离上小于等于k的有没有0换即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100003,M=200003;
struct node{
int to,w,ne;
}e[M<<1];
int h1[N],hn[N],dis[N],n,m,K,P,in[N][51],f[N][51],tot,T,u,v,w;
char ss[1<<17],*A=ss,*B=ss;
#define gc getchar
template<class T>inline void read(T&x){
char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c=='-')y=-1;x=c^48;
while(c=gc(),47<c&&c<58)x=(x<<1)+(x<<3)+(c^48);x*=y;
}
namespace segment{
int tr[N<<2],seg;
void Set(int n){seg=1;while (seg<=n) seg<<=1;seg--;tr[0]=N-1;}
void clr(){for (int i=1;i<=(seg<<1|1);i++) tr[i]=0;}
int cmp(int a,int b){return dis[a]<dis[b]?a:b;}
void mdy(int x,int w){for (int i=x+seg;dis[tr[i]]>w;i>>=1) tr[i]=x;dis[x]=w;}
void del(int x){tr[x+=seg]=0;x>>=1;while (x) tr[x]=cmp(tr[x<<1],tr[x<<1|1]),x>>=1;}
}
using namespace segment;
void add(int u,int v,int w,int *h){
e[++tot]=(node){v,w,h[u]};
h[u]=tot;
}
void mod(int &x){x-=x>=P?P:0;}
void dij(){
memset(dis,63,(n+1)*4);
clr();mdy(n,0);
for (int T=1;T<=n;T++){
int u=tr[1];del(u);
for (int i=hn[u],v;i;i=e[i].ne)
if (dis[v=e[i].to]>dis[u]+e[i].w) mdy(v,dis[u]+e[i].w);
}
}
int dfs(int u,int k){
if (in[u][k]) return -1;
if (f[u][k]) return f[u][k];
in[u][k]=1;
f[u][k]=u==n?1:0;
for (int i=h1[u],v,tp,w;i;i=e[i].ne){
if ((tp=dis[v=e[i].to]-dis[u]+e[i].w)<=k){
if ((w=dfs(v,k-tp))==-1) return f[u][k]=-1;
mod(f[u][k]+=w);
}
}
in[u][k]=0;
return f[u][k];
}
int main(){
read(T);
while (T--){
read(n);read(m);read(K);read(P);
tot=0;Set(n);
memset(f,0,sizeof(f));
memset(in,0,sizeof(in));
memset(h1,0,(n+1)*4);
memset(hn,0,(n+1)*4);
while (m--){
read(u);read(v);read(w);
add(u,v,w,h1);add(v,u,w,hn);
}
dij();
printf("%d\n",dfs(1,K));
}
}