我们发现每次加入的边模p的余数是相同的,很容易想到对p分块
当p>sqrt(n)时,最多会连出O(sqrt(n))条边,直接连边即可
当p<=sqrt(n)时,
把图分成sqrt(n)+1层,最底层表示原来的节点
从下往上依次表示p=1、2、3……时的节点
对于每个p,把模p的余数相同的点顺次连接
对于每个读入的p,从最底层向对应的第p层的节点连一条权值为0的边即可
据说spfa快?
还算是道不错的题
当p>sqrt(n)时,最多会连出O(sqrt(n))条边,直接连边即可
当p<=sqrt(n)时,
把图分成sqrt(n)+1层,最底层表示原来的节点
从下往上依次表示p=1、2、3……时的节点
对于每个p,把模p的余数相同的点顺次连接
对于每个读入的p,从最底层向对应的第p层的节点连一条权值为0的边即可
据说spfa快?
还算是道不错的题
O(3*n*sqrt(n)+2*num*sqrt(n)+(m-num))
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 30010
#define N 15000010
#define inf 1000000000
using namespace std;
int head[110*maxn],to[N],len[N],next[N],dis[110*maxn];
bool vis[110*maxn];
int pos[110][maxn],q[110*maxn];
int n,m,num,block,cnt,s,t;
void addedge(int x,int y,int z)
{
num++;to[num]=y;len[num]=z;next[num]=head[x];head[x]=num;
}
void spfa()
{
for (int i=0;i<=cnt;i++) dis[i]=inf;
int l=0,r=1;
q[1]=s;vis[s]=1;dis[s]=0;
while (l!=r)
{
l++;if (l==110*maxn) l=0;
int x=q[l];
for (int p=head[x];p;p=next[p])
if (dis[x]+len[p]<dis[to[p]])
{
dis[to[p]]=dis[x]+len[p];
if (!vis[to[p]])
{
r++;if (r==110*maxn) r=0;
vis[to[p]]=1;q[r]=to[p];
}
}
vis[x]=0;
}
}
int main()
{
scanf("%d%d",&n,&m);
cnt=n-1;block=min((int)sqrt(n),100);
for (int i=1;i<=block;i++)
for (int j=0;j<i;j++)
for (int k=j;k<n;k+=i)
{
pos[i][k]=++cnt;
addedge(cnt,k,0);
if (k>=i) addedge(cnt-1,cnt,1),addedge(cnt,cnt-1,1);
}
for (int i=1;i<=m;i++)
{
int B,p;
scanf("%d%d",&B,&p);
if (i==1) s=B;
if (i==2) t=B;
if (p<=block) addedge(B,pos[p][B],0);
else
{
for (int j=1;B+j*p<n;j++) addedge(B,B+j*p,j);
for (int j=1;B-j*p>=0;j++) addedge(B,B-j*p,j);
}
}
spfa();
if (dis[t]!=inf) printf("%d\n",dis[t]); else printf("-1\n");
return 0;
}