洛谷P3645 [APIO2015]雅加达的摩天楼(最短路+分块)
这最短路的建图怎么和网络流一样玄学……
一个最朴素的想法是从每一个点向它能到达的所有点连边,边权为跳的次数,然后跑最短路(然而边数是$O(n^2)$除非自创复杂度比spfa和dijkstra还有优秀的做法否则根本过不了)
那么考虑一下分块
把每一座建筑拆成$O(\sqrt{n})$层,第$i$层代表在这一层只能每一步跳$i$个建筑,然后这一层每一个建筑向它能到达的点连双向边
然后每一层每一个建筑向底层连边,代表如果这里有其他狗就可以更换跳的步数
然后考虑每一只狗,如果它每一步跳的步数小于$\sqrt{n}$,那么直接把它向对应建筑对应层数的点连边
如果大于$\sqrt{n}$,直接暴力向它能到达的点连边,那么连边的条数不会超过$O(logn)$
所以实际的边数不会超过$O(nlogn)$,那么就跑一个最短路就好了
然而似乎spfa诈尸……这题卡dijkstra只能用spfa
然而数据很诡异块的大小得调成$min(\sqrt{n},100)$
差不多就这样
1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<cmath> 7 #define id(i,k) ((n*k)+i) 8 using namespace std; 9 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 10 char buf[1<<21],*p1=buf,*p2=buf; 11 template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;} 12 inline int read(){ 13 #define num ch-'0' 14 char ch;bool flag=0;int res; 15 while(!isdigit(ch=getc())) 16 (ch=='-')&&(flag=true); 17 for(res=num;isdigit(ch=getc());res=res*10+num); 18 (flag)&&(res=-res); 19 #undef num 20 return res; 21 } 22 const int N=30005,M=N*500; 23 int ver[M],Next[M],head[M],edge[M],tot; 24 int vis[M],dis[M];queue<int> q; 25 int b[N],p[N],n,m,block,ans; 26 inline void add(int u,int v,int e){ 27 ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e; 28 } 29 int spfa(int s,int t){ 30 memset(dis,0x3f,sizeof(dis)); 31 memset(vis,0,sizeof(vis)); 32 q.push(s),vis[s]=1,dis[s]=0; 33 while(!q.empty()){ 34 int u=q.front();q.pop(),vis[u]=0; 35 for(int i=head[u];i;i=Next[i]){ 36 int v=ver[i]; 37 if(cmin(dis[v],dis[u]+edge[i])) 38 if(!vis[v]) q.push(v),vis[v]=1; 39 } 40 } 41 return dis[t]; 42 } 43 int main(){ 44 // freopen("testdata.in","r",stdin); 45 n=read(),m=read(); 46 block=min(100,(int)sqrt(n)); 47 for(int i=1;i<=block;++i){ 48 for(int j=0;j+i<n;++j){ 49 add(id(j,i),id(j+i,i),1), 50 add(id(j+i,i),id(j,i),1); 51 } 52 for(int j=0;j<n;++j) add(id(j,i),j,0); 53 } 54 for(int i=0;i<m;++i){ 55 b[i]=read(),p[i]=read(); 56 if(p[i]<=block) add(b[i],id(b[i],p[i]),0); 57 else{ 58 for(int j=b[i]-p[i];j>=0;j-=p[i]) 59 add(b[i],j,(b[i]-j)/p[i]); 60 for(int j=b[i]+p[i];j<n;j+=p[i]) 61 add(b[i],j,(j-b[i])/p[i]); 62 } 63 } 64 ans=spfa(b[0],b[1]); 65 printf("%d\n",ans!=0x3f3f3f3f?ans:-1); 66 return 0; 67 }
深深地明白自己的弱小