【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 }