【NOIP2017】逛公园 最短路+DP

诶,去年场上不会处理$0$的环,只拿了$60$有点可惜。

我们先不管边边权为$0$的边。

我们先跑一次最短路,令$dis[u]$表示从$1$至$u$的最短路的长度。

那么根据题目的要求,从起点走到$u$号点的路径长度只可能在区间$[dis[u],dis[u]+k]$中。

令$f[i][j]$表示当前从起点走到$i$,行走的路程为$dis[i]+j$的方案数。

不妨发现这个东西可以通过类似分层图最短路的方式进行更新,然后就直接更新就行了。

然而这一题中有部分点存在边权为$0$的边,一旦走入一个$0$环的话采用上述的方法会$TLE$。

于是我们把上面的通过分层图最短路的更新方式,更换为记忆化搜索,我们在当前搜索的路径上打上标记,然后若走到之前标记过的点,那么直接输出$-1$退出即可。这种方式可以有效避免将无需经过的零环纳入考虑范围内。

(可能是个人写法的原因),经过$1$的零环需要特判,直接在最短路里判掉就好了。

然后就没了。时间复杂度:$O(mk+m log n)$。

 1 #include<bits/stdc++.h>
 2 #define M 100005
 3 using namespace std;
 4 
 5 struct edge{int u,v,next;}e[M*4]={0}; int head[M]={0},head1[M]={0},use=0;
 6 void add(int x,int y,int z){use++;e[use].u=y;e[use].v=z;e[use].next=head[x];head[x]=use;}
 7 void add1(int x,int y,int z){use++;e[use].u=y;e[use].v=z;e[use].next=head1[x];head1[x]=use;}
 8 int n,m,k,MOD;
 9 
10 struct node{
11     int x,dis;
12     node(){x=dis=0;}
13     node(int xx,int ddis){x=xx; dis=ddis;}
14     friend bool operator <(node a,node b){return a.dis>b.dis;}
15 }a[M];
16 priority_queue<node> q;
17 int vis[M]={0},dis[M]={0},v[M][52]={0},f[M][52]={0},in[M][52]={0};
18 int dij(){
19     q.push(node(1,0));
20     while(!q.empty()){
21         node U=q.top(); q.pop();
22         int u=U.x;
23         if(vis[u]) continue;
24         vis[u]=1; dis[u]=U.dis;
25         for(int i=head[u];i;i=e[i].next){ 
26             if(dis[u]+e[i].v==0&&e[i].u==1) return 0;
27             if(!vis[e[i].u]) q.push(node(e[i].u,dis[u]+e[i].v));
28         }
29     }
30     return 1;
31 }
32 
33 int dfs(int x,int p){
34     if(f[x][p]!=-1) return f[x][p];
35     if(in[x][p]) return -233;
36     int res=0; in[x][p]=1;
37     for(int i=head1[x];i;i=e[i].next){
38         int v=dis[x]-dis[e[i].u]+p-e[i].v;
39         if(0<=v&&v<=k){
40             int now=dfs(e[i].u,v);
41             if(now==-233) return -233;
42             res=(res+now)%MOD;
43         }
44     }
45     f[x][p]=res; in[x][p]=0;
46     return res;
47 }
48 
49 int Main(){
50     memset(vis,0,sizeof(vis)); memset(dis,0,sizeof(dis)); 
51     memset(f,-1,sizeof(f)); memset(e,0,sizeof(e));
52     memset(head,0,sizeof(head)); memset(head1,0,sizeof(head1)); use=0;
53     memset(in,0,sizeof(in));
54     memset(v,0,sizeof(v));
55     scanf("%d%d%d%d",&n,&m,&k,&MOD);
56     for(int i=1;i<=m;i++){
57         int x,y,z; scanf("%d%d%d",&x,&y,&z);
58         add(x,y,z);
59         add1(y,x,z);
60     }
61     if(dij()==0){printf("-1\n"); return 0;}
62     int ans=0; f[1][0]=1;
63     for(int i=0;i<=k;i++){
64         int now=dfs(n,i);
65         if(now==-233){
66             printf("-1\n");
67             return 0;
68         }
69         ans=(ans+now)%MOD;
70     }
71     printf("%d\n",ans);
72 }
73 
74 int main(){
75     int cas; cin>>cas;
76     while(cas--) Main();
77 }

 

posted @ 2018-10-10 21:48  AlphaInf  阅读(220)  评论(0编辑  收藏  举报