最坏情况有n^2条边,一般最短路算法都不能过。
考虑用分块的思想来优化建图。
Pi>sqrt(n),暴力加入每一条边,每次最多sqrt(n)条边。
Pi≤sqrt(n),对于每个点添加sqrt(n)个辅助点,一层一层走,边数是O(n*sqrt(n))的。
总边数和总点数都是O(n*sqrt(n))。
块不能太大 不然爆内存 块最大100
#include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #define V G[p].v #define P(x,y) ((x)*n+(y)) using namespace std; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } return *p1++; } inline void read(int &x) { char c=nc(),b=1; for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; } const int N=4000005; const int M=15000005; struct edge{ int v,w; int next; }; edge G[M]; int head[N],inum; inline void add(int u,int v,int w,int p){ G[p].v=v; G[p].w=w; G[p].next=head[u]; head[u]=p; } const int ND=N; int Q[ND],l,r; int ins[N],dis[N]; int n,m,S,T,B; int b[30005],p[30005]; inline void SPFA(int S){ int u; for (int i=1;i<=P(B,n);i++) dis[i]=1<<30,ins[i]=0; l=r=-1; dis[S]=0; Q[(++r)%ND]=S; ins[S]=1; while (l!=r){ u=Q[(++l)%ND]; ins[u]=0; for (int p=head[u];p;p=G[p].next) if (dis[V]>dis[u]+G[p].w) { dis[V]=dis[u]+G[p].w; if (!ins[V]) Q[(++r)%ND]=V,ins[V]=1; } } } int main() { freopen("t.in","r",stdin); freopen("t.out","w",stdout); read(n); read(m); for (int i=1;i<=m;i++) read(b[i]),b[i]++,read(p[i]); S=b[1]; T=b[2]; B=min((int)sqrt(n),100); for (int i=1;i<=B;i++) for (int j=1;j<=n;j++) add(P(i,j),j,0,++inum); for (int i=1;i<=B;i++) for (int j=1;j<=n-i;j++) add(P(i,j),P(i,j+i),1,++inum),add(P(i,j+i),P(i,j),1,++inum); for (int i=1;i<=m;i++) if (p[i]<=B) add(b[i],P(p[i],b[i]),0,++inum); else { for(int j=1;b[i]+j*p[i]<=n;j++) add(b[i],b[i]+j*p[i],j,++inum); for(int j=1;b[i]-j*p[i]>=1;j++) add(b[i],b[i]-j*p[i],j,++inum); } SPFA(S); if (dis[T]==1<<30) printf("-1\n"); else printf("%d\n",dis[T]); return 0; }