洛谷P3953 [NOIP2017]逛公园

 

K<=50,感觉可以DP

先建反图求出从n到各个点的最短路,然后在正图上DP

设f[当前点][比最短路多走的距离]=方案数

转移显然是 $f[v][res]=\sum f[u][res+tmp]$  tmp是从v到u比最短路多走的路程

注意如果图中有0环,则有无穷多种方案。

判0环可以DFS判,也可以把最短路边和0权边建在一个新图上,用拓扑排序判(显然前者更简单)

 

  1 /*by SilverN*/
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cstdio>
  5 #include<cmath>
  6 #include<cstring>
  7 #include<queue>
  8 using namespace std;
  9 const int mxn=100010;
 10 int read(){
 11     int x=0,f=1;char ch=getchar();
 12     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 13     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
 14     return x*f;
 15 }
 16 struct edge{
 17     int v,nxt,w;
 18 }e[mxn<<1],er[mxn<<1];
 19 int hd[mxn],mct=0;
 20 int hdr[mxn],rct=0;
 21 void add_edge(int u,int v,int w){
 22     e[++mct].v=v;e[mct].nxt=hd[u];e[mct].w=w;hd[u]=mct;return;
 23 }
 24 void add_edgeR(int u,int v,int w){
 25     er[++rct].v=v;er[rct].nxt=hdr[u];er[rct].w=w;hdr[u]=rct;return;
 26 }
 27 int dis[mxn];
 28 bool inq[mxn];
 29 int vis[mxn];
 30 queue<int>q;
 31 int n,m,K,P;
 32 void SPFA(int s){//反图SPFA 
 33     memset(dis,0x3f,sizeof(dis));
 34     dis[s]=0;
 35     q.push(s);
 36     while(!q.empty()){
 37         int u=q.front();q.pop();inq[u]=0;
 38         for(int i=hdr[u];i;i=er[i].nxt){
 39             int v=er[i].v;
 40             if(dis[v]>dis[u]+er[i].w){
 41                 dis[v]=dis[u]+er[i].w;
 42                 if(!inq[v]){ inq[v]=1;    q.push(v); }
 43             }
 44         }
 45     }
 46     return;
 47 }
 48 inline void add(int &a,int b){
 49     a+=b; a=(a>P)?(a-P):a; return;
 50 }
 51 int f[mxn][51];
 52 int ind[mxn];//时间戳
 53 int cir;
 54 int DP(int u,int res){
 55 //    printf("in:%d %d\n",u,res);
 56     if(vis[u] && ind[u]==res){cir=1;return 0;}
 57     if(f[u][res]!=-1)return f[u][res];
 58     int tmp=0;
 59     if(u==n && !res)tmp++;//return 1影响判环 
 60     vis[u]++;
 61     ind[u]=res;
 62     //
 63     for(int i=hd[u];i;i=e[i].nxt){
 64         int v=e[i].v;
 65         int x=res-(e[i].w-(dis[u]-dis[v]));
 66         if(x>=0 && x<=K)
 67             add(tmp,DP(v,x));
 68         if(cir)return 0;
 69     }
 70     //
 71     vis[u]--;
 72     ind[u]=0;
 73     return f[u][res]=tmp;
 74 }
 75 void init(){
 76     memset(f,-1,sizeof f);
 77     memset(hd,0,sizeof hd);
 78     memset(hdr,0,sizeof hdr);
 79     memset(ind,0,sizeof ind);
 80     memset(vis,0,sizeof vis);
 81     mct=0;rct=0;cir=0;
 82     return;
 83 }
 84 int main(){
 85     int i,j,u,v,w,ans;
 86     int T=read();
 87     while(T--){
 88         init();
 89         n=read();m=read();K=read();P=read();
 90         for(i=1;i<=m;i++){
 91             u=read();v=read();w=read();
 92             add_edge(u,v,w);
 93             add_edgeR(v,u,w);
 94         }
 95         SPFA(n);
 96         ans=0;
 97         for(i=0;i<=K;i++){
 98             add(ans,DP(1,i));
 99             if(cir)break;
100         }
101         if(cir) puts("-1");
102             else printf("%d\n",ans);
103     }
104     return 0;
105 }

 

posted @ 2018-06-21 21:29  SilverNebula  阅读(562)  评论(0编辑  收藏  举报
AmazingCounters.com