【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 行,每行一个整数代表答案。

输入样例:

  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

题解:

  首先可以想到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 }

 

posted @ 2017-11-25 18:18  LittleOrange  阅读(1138)  评论(0编辑  收藏  举报