luogu P3645 [APIO2015]雅加达的摩天楼
题面传送门
n这么小就很诡异。
考虑设阈值\(B\)
如果当前的\(P_i>B\)那么直接暴力建边就好了,这时候的边数是\(O(\frac{n}{B})\)的。
如果当前的\(P_i<B\)那么放到一起处理,发现如果\(P\)是相同的,那么位置\(\bmod p\)相同的全部位置只要在左右之间建边就好了,其它边是可以迭代进去的,也就是从左往右一条和从右往左一条,这里的边数是\(O(nB)\)的。
然后跑最短路就好了,总复杂度\(O(SPFA\times (m\frac{n}{B}+nB))\)
取\(B=sqrt m\)最优复杂度\(O(n\sqrt m\times SPFA)\)
实测跑得飞快,直接rk2
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define lb long db
#define N 30000
#define K 400
#define mod 20170408
#define eps (1e-4)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Pc(x) putchar(x)
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (m*(x-1)+(y))
using namespace std;queue<int> Q;
int n,m,k,x,y,S,T;ll d[N+5];vector<int> Id[N+5],F[N+5];
struct yyy{int to,w,z;}tmp;struct ljb{int head,h[N+5];yyy f[N*K+5];I void add(int x,int y,int z){f[++head]=(yyy){y,z,h[x]};h[x]=head;}}s;
I void insert(int x,int l,int r,int z){re int i;for(i=x-z;i>=l;i-=z) s.add(x,i,(x-i)/z);for(i=x+z;i<=r;i+=z) s.add(x,i,(i-x)/z);}
int main(){
freopen("1.in","r",stdin);
re int i,j,h,z;scanf("%d%d",&n,&m);k=sqrt(m);for(i=0;i<m;i++) {
scanf("%d%d",&x,&y);!i&&(S=x);(i==1)&&(T=x);if(y<=k){Id[y].push_back(x);continue;}insert(x,0,n-1,y);
}for(i=1;i<=k;i++){
for(j=0;j<i;j++) F[j].clear();for(j=0;j<Id[i].size();j++) F[Id[i][j]%i].push_back(Id[i][j]);for(j=0;j<i;j++) {
F[j].push_back(0);F[j].push_back(n-1);sort(F[j].begin(),F[j].end());for(h=1;h<F[j].size()-1;h++)insert(F[j][h],F[j][h-1],F[j][h+1],i);
}
}Me(d,0x3f);d[S]=0;Q.push(S);while(!Q.empty())for(x=Q.front(),Q.pop(),i=s.h[x];i;i=tmp.z)tmp=s.f[i],d[tmp.to]>d[x]+tmp.w&&(Q.push(tmp.to),d[tmp.to]=d[x]+tmp.w);printf("%lld\n",d[T]>1e18?-1:d[T]);
}