2018多校第三场 hdu6331 M :Walking Plan
题目链接 hdu6331
自我吐槽,这场多校大失败,开局签到因输入输出格式写错,wa了3发。队友C题wa了1个小时,还硬说自己写的没错,结果我随便造了个小数据,他都没跑对。然后跑对了后又进入了无限的卡常之中,幸好最后卡过去了。
G题我头铁写了个单侧凸壳,不对输入数据判重,而是在加入凸壳时判断,结果wa到比赛结束,进而导致I没时间写。M题开局看错题意直接丢了。最后从30多名掉到了150。这大概就是菜得安详吧 ̄ω ̄=
题意
给定n个点m条有向边,q次查询,每次查询问走至少k条边的,s到t的最短路径的长度,边可以重复走。
q=100000,n=50,m<=10000,k<=10000..
这题用矩阵快速幂啥的复杂度爆炸
细节
1.首先最基本的,要先对边去重留下最短边。
2.这里有个细节要注意一下,在某些情况下 s 到t距离,只有在走过的边数达到某个周期后才有解。
比如n个点组成一条有向环,则1到2距离只有在走过数边数k=1,n+1,2n+1 。周期也就是环的长度。
所以查询k时,答案的路径边数范围会在k到n+k之间。
3.任意两点的最短距离的边数小于n
预处理
首先预处理两个数组。g[k][s][t] 代表恰好走k条边后s到t的最短路径。
k=1,2,……10000.
这个用Floyd跑全部数据复杂度要k*n*n*n=12.5亿=GG。 这显然是不行的,所以只能分块,我取块长为100,即只让k=1,2,3,……100。但相邻二维数组之间间隔100条边。则复杂降低至1250万。
接着就是怎么处理块内了。
dis[k][s][t] 代表走大于等于k条边后s到t的最短路径。
k=1,2,……100 。
这个处理方式时先用floyd跑到k=200, 再逆向for 令dis[k][s][t]=min(dis[k][s][t],dis[k+1][s][t])这样就能保证在k=1,2,3……100时答案是正确的。
因为大于等于k的最坏情况,就是从s到达了点z,接着还要跑(z,t)之间的最短路才能到达t. 而最短路D的边数最多就n-1。 其实更新n项后就一定就正确了。所以溢出100项,只是写的顺手。
查询
对于k<=100的查询我们可以直接用dis数组回答复杂度O(1)
对于k>100的查询,令 k=q*100+r 其中1<=r<=100
则答案就是min(g[q][s][z]+dis[r][z][t]) 。其中z是我们枚举的中间点。 这样就能保证查询到的答案是大于等于k的,且答案是正确的。单次查询复杂度O(n)
AC代码
、
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<algorithm> 5 using namespace std; 6 int dis[202][52][52]; 7 int g[102][52][52]; 8 int INF=1e9,n; 9 int get(int s,int t,int k) 10 { 11 if(k<=100) 12 { 13 return dis[k][s][t]; 14 } 15 else 16 { 17 register int i,q,r,ans=INF; 18 19 q=(k-1)/100; 20 r=k-100*q; 21 //printf("%d %d \n",q,r); 22 for(i=1; i<=n; i++) 23 { 24 ans=min(ans,dis[r][s][i]+g[q][i][t]); 25 } 26 return ans; 27 } 28 } 29 int main() 30 { 31 int m,t,x,y,w,q,ans; 32 register int i,j,k,l; 33 scanf("%d",&t); 34 while(t--) 35 { 36 scanf("%d%d",&n,&m); 37 memset(dis,63,sizeof(dis)); 38 memset(g,63,sizeof(g)); 39 INF=dis[0][0][0]; 40 // printf("%d\n",INF); 41 for(i=1; i<=n; i++) 42 { 43 for(j=1; j<=n; j++) 44 { 45 dis[1][i][j]=INF; 46 } 47 } 48 while(m--) 49 50 { 51 scanf("%d%d%d",&x,&y,&w); 52 dis[1][x][y]=min(dis[1][x][y],w); 53 } 54 for(i=2; i<=200; i++) 55 { 56 for(j=1; j<=n; j++) 57 { 58 for(k=1; k<=n; k++) 59 { 60 for(l=1; l<=n; l++) 61 { 62 dis[i][j][k]=min( dis[i][j][k],dis[i-1][j][l]+dis[1][l][k]); 63 } 64 } 65 } 66 } 67 for(i=1; i<=n; i++) 68 { 69 for(j=1; j<=n; j++) 70 { 71 g[1][i][j]=dis[100][i][j]; 72 } 73 } 74 for(i=2; i<=100; i++) 75 { 76 for(j=1; j<=n; j++) 77 { 78 for(k=1; k<=n; k++) 79 { 80 for(l=1; l<=n; l++) 81 { 82 g[i][j][k]=min( g[i][j][k],g[i-1][j][l]+g[1][l][k]); 83 } 84 } 85 } 86 } 87 for(i=199; i>=1; i--) 88 { 89 for(j=1; j<=n; j++) 90 { 91 for(k=1; k<=n; k++) 92 { 93 dis[i][j][k]=min( dis[i][j][k],dis[i+1][j][k]); 94 } 95 } 96 } 97 scanf("%d",&q); 98 while(q--) 99 { 100 scanf("%d%d%d",&x,&y,&k); 101 ans=get(x,y,k); 102 if(ans<=INF/10) 103 { 104 printf("%d\n",ans); 105 } 106 else 107 { 108 puts("-1"); 109 } 110 } 111 } 112 113 return 0; 114 }
话说我写的这么暴力,在ac代码里居然不算很慢的︿( ̄︶ ̄)︿