解析
容易想到dp
先跑一遍最短路把每个点的dis求出来
设计dpu,xdp_{u,x}dpu,x表示结点u多走了x的方案数
dp按照dis升序排列后,从前到后转移即可
如果有0边,求出只有0边时的拓扑序,作为第二关键字进行排序
关于0环,第一篇题解的拓扑似乎是假的…
利用dfs和记搜就是真的了
但是不想改了
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define debug(a,b) fprintf(stderr,a,b)
const int N=1e6+100;
inline ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;
}
int n,m,k,mod;
#define pr pair<int,int>
#define mkp make_pair
struct graph{struct edge{int to,nxt,w;}p[N<<1];int fi[N],cnt;int dis[N],du[N];bool tag[N];int id[N];int tot;priority_queue<pr,vector<pr>,greater<pr> >pq;int vis[N];queue<int>q;void init(){tot=0;memset(fi,-1,sizeof(fi));cnt=-1;memset(tag,0,sizeof(tag));memset(vis,0,sizeof(vis));memset(du,0,sizeof(du));memset(dis,0x3f,sizeof(dis));}inline void addline(int x,int y,int w){//if(fi[x]>1e7)//fprintf(stderr,"%d %d %d %d\n",x,y,w,fi[x]);p[++cnt]=(edge){y,fi[x],w};fi[x]=cnt;if(w==0) du[y]++;return;}void dij(int x){pq.push(mkp(0,x));dis[x]=0;while(!pq.empty()){int now=pq.top().second;pq.pop();//fprintf(stderr,"now=%d\n",now);if(vis[now]) continue;vis[now]=1;for(int i=fi[now];~i;i=p[i].nxt){int to=p[i].to;if(dis[to]>dis[now]+p[i].w){dis[to]=dis[now]+p[i].w;//fprintf(stderr,"%d->%d %d\n",now,to,dis[to]);pq.push(mkp(dis[to],to));//fprintf(stderr," ok\n");}//fprintf(stderr,"nxt=%d\n",p[i].nxt);}//fprintf(stderr,"------end\n");}return;}void topu(){//fprintf(stderr,"ok\n");for(int i=1;i<=n;i++){if(!du[i]) q.push(i);}while(!q.empty()){int now=q.front();q.pop();tag[now]=1;id[now]=++tot;for(int i=fi[now];~i;i=p[i].nxt){int to=p[i].to;if(!p[i].w){--du[to];if(!du[to]){q.push(to);//fprintf(stderr,"%d\n",to);}}}}}
}g1,g2;int dp[N][52];
int ans;
int que[N];
bool cmp(int x,int y){if(g1.dis[x]!=g1.dis[y])return g1.dis[x]<g1.dis[y];else return g1.id[x]<g1.id[y];
}
int main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifint T=read();while(T--){g1.init();g2.init();n=read();m=read();k=read();mod=read();for(int i=1;i<=m;i++){int x=read(),y=read(),w=read();g1.addline(x,y,w);g2.addline(y,x,w);}g1.dij(1);g2.dij(n);g1.topu();g2.topu();int flag=0;for(int i=1;i<=n;i++){if(g1.tag[i]||g2.tag[i]) continue;if(g1.dis[i]+g2.dis[i]<=g1.dis[n]+k){flag=1;break;}}if(flag){printf("-1\n");continue;}for(int i=1;i<=n;i++) que[i]=i;sort(que+1,que+1+n,cmp);//for(int i=1;i<=n;i++) printf("%d(%d) ",que[i],g1.dis[que[i]]);//putchar('\n');memset(dp,0,sizeof(dp));int ans=0;dp[1][0]=1;for(int w=0;w<=k;w++){for(int pp=1;pp<=n;pp++){int now=que[pp];if(dp[now][w]==0) continue;for(int i=g1.fi[now];~i;i=g1.p[i].nxt){int to=g1.p[i].to;int o=w+g1.p[i].w+g1.dis[now]-g1.dis[to];if(o>k) continue;(dp[to][o]+=dp[now][w])%=mod;}}(ans+=dp[n][w])%=mod;}printf("%d\n",ans);}return 0;
}
/**/