1. 程式人生 > >bzoj 4070: [Apio2015]雅加達的摩天樓【spfa】

bzoj 4070: [Apio2015]雅加達的摩天樓【spfa】

n) amp getchar source UC pty 網絡 abs char

明明是個最短路卻有網絡流一樣的神建圖= A =
首先要是暴力建圖的話最壞有O(nm)條邊。所以優化建圖。
考慮分塊思想,設bs=sqrt(n),對於p大於bs的,直接連邊即可,最多有sqrt(n)條,註意邊權不全是1了,因為要從b走過去;對於p小於等於bs,先把每棟樓建sqrt個輔助點,然後這些輔助點向原點連邊,其中i點的輔助點j表示i棟樓可以跳p步。這些輔助點同層之間連雙向邊。然後對於一只狗,直接連接b點與b點的第p輔助點即可。
spfa即可
然而事實證明bs取sqrt甚至會re,直接取100會好一些

#include<iostream>
#include<cstdio>
#include<cmath> #include<queue> using namespace std; const int N=30005,inf=1e9; int n,m,b[N],p[N],h[N*200],cnt,bs,dis[N*200]; bool v[N*200]; queue<int>q; struct qwe { int ne,to,va; }e[N*500]; int read() { int r=0,f=1; char p=getchar(); while(p>‘9‘||p<‘0‘) { if
(p==‘-‘) f=-1; p=getchar(); } while(p>=‘0‘&&p<=‘9‘) { r=r*10+p-48; p=getchar(); } return r*f; } void add(int u,int v,int w) { cnt++; e[cnt].ne=h[u]; e[cnt].to=v; e[cnt].va=w; h[u]=cnt; } int main() { n=read(),m=read(); bs=min((int
)sqrt(n),100); for(int i=1;i<=m;i++) b[i]=read()+1,p[i]=read(); for(int i=1;i<=bs;i++) for(int j=1;j<=n;j++) add(i*n+j,j,0); for(int i=1;i<=bs;i++) for(int j=1;j<=n-i;j++) add(i*n+j,i*n+j+i,1),add(i*n+j+i,i*n+j,1); for(int i=1;i<=m;i++) { if(p[i]<=bs) add(b[i],p[i]*n+b[i],0); else { // for(int j=b[i]-b[i]/p[i]*p[i];j<=n;j+=p[i]) // add(b[i],j,abs(b[i]-j)/p[i]); for(int j=1;b[i]+j*p[i]<=n;j++) add(b[i],b[i]+j*p[i],j); for(int j=1;b[i]-j*p[i]>=1;j++) add(b[i],b[i]-j*p[i],j); } } for(int i=1;i<=n*200;i++) dis[i]=inf; int s=b[1],t=b[2]; dis[s]=0,v[s]=1,q.push(s); while(!q.empty()) { int u=q.front();//cerr<<u<<endl; q.pop(); v[u]=0; for(int i=h[u];i;i=e[i].ne) if(dis[e[i].to]>dis[u]+e[i].va) { dis[e[i].to]=dis[u]+e[i].va; if(!v[e[i].to]) { v[e[i].to]=1; q.push(e[i].to); } } } printf("%d\n",dis[t]==inf?-1:dis[t]); return 0; }

bzoj 4070: [Apio2015]雅加達的摩天樓【spfa】