1. 程式人生 > >解題:APIO 2015 雅加達的摩天大樓

解題:APIO 2015 雅加達的摩天大樓

題面

分塊思想+最短路

發現對於步長小的doge會連出很多邊,很容易導致大量的重邊,於是對doge們根據步長分塊討論:根據步長建出分層圖,然後把步長不超過某個值的doge們連到對應層上的點上,其餘的doge暴力連邊,最後在分層圖的每層中間把邊連滿然後跑最短路,這樣邊數是$O(n$ $sqrt(n))$的

注意:此題資料非常辣雞,如果確定演算法是對的然後被卡了可以問候一下出題人然後摸走,沒啥調的必要,主要體現在:

1.只對步長為1的doge們建分層圖,然後其餘doge們暴力連邊,會跑的飛起(吊打“正常”連法)

2.上文的“正常”是指將塊大小與$100$取min,不然會連爆。那為什麼是和$100$取min呢?因為試出來是這樣的,呵呵

3.不知道資料怎麼造的,好像太**稀疏了,以至於佇列優化的Bellman-Ford跑的比Dijkstra還快,甚至Dijkstra會被卡常

心裡默默問候無良出題人

 1 // luogu-judger-enable-o2
 2 #include<cmath>
 3 #include<queue>
 4 #include<cstdio>
 5 #include<cstring>
 6 #include<algorithm>
 7 using namespace std;
 8 const int N=2500005,M=8000005
,inf=0x3f3f3f3f; 9 struct a{int node,dist;}; 10 bool operator <(a x,a y) 11 { 12 return x.dist>y.dist; 13 } 14 priority_queue<a> hp; 15 int n,m,c,t1,t2,st,ed,cnt,sqr; 16 int dis[N],vis[N],val[2*M]; 17 int p[N],noww[2*M],goal[2*M]; 18 void link(int f,int t,int v) 19 { 20 noww[++cnt]=p[f],p[f]=cnt;
21 goal[cnt]=t,val[cnt]=v; 22 } 23 void Dijkstra() 24 { 25 register int i; 26 memset(dis,0x3f,sizeof dis); 27 hp.push((a){st,0}),dis[st]=0; 28 while(!hp.empty()) 29 { 30 a tt=hp.top(); hp.pop(); int tn=tt.node; 31 if(vis[tn]) continue; vis[tn]=true; 32 for(i=p[tn];i;i=noww[i]) 33 if(dis[goal[i]]>dis[tn]+val[i]) 34 { 35 dis[goal[i]]=dis[tn]+val[i]; 36 hp.push((a){goal[i],dis[goal[i]]}); 37 } 38 } 39 } 40 int main () 41 { 42 register int i,j; 43 scanf("%d%d",&n,&m),sqr=min((int)sqrt(n),80); 44 for(i=1;i<=m;i++) 45 { 46 scanf("%d%d",&t1,&t2),t1++; 47 if(i==1) st=t1; if(i==2) ed=t1; 48 if(t2<=sqr) 49 link(t1,t2*n+t1,0); 50 else 51 { 52 for(j=t1-t2,c=1;j>=1;j-=t2,c++) link(t1,j,c); 53 for(j=t1+t2,c=1;j<=n;j+=t2,c++) link(t1,j,c); 54 } 55 } 56 for(i=1;i<=sqr;i++) 57 for(j=1;j<=n;j++) 58 { 59 if(j-i>=1) link(i*n+j,i*n+j-i,1); 60 if(j+i<=n) link(i*n+j,i*n+j+i,1); 61 link(i*n+j,j,0); 62 } 63 Dijkstra(); dis[ed]==inf?printf("-1"):printf("%d",dis[ed]); 64 return 0; 65 }
View Code