4070: [Apio2015]雅加达的摩天楼
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 189 Solved: 70
Description
印尼首都雅加达市有 N 座摩天楼,它们排列成一条直线,我们从左到右依次将它们编号为 0 到 N?1。除了这 N 座摩天楼外,雅加达市没有其他摩天楼。
有 M 只叫做 “doge” 的神秘生物在雅加达市居住,它们的编号依次是 0 到 M?1。编号为 i 的 doge 最初居住于编号为 Bi 的摩天楼。每只 doge 都有一种神秘的力量,使它们能够在摩天楼之间跳跃,编号为 i 的 doge 的跳跃能力为 Pi (Pi>0)。
在一次跳跃中,位于摩天楼 b 而跳跃能力为 p 的 doge 可以跳跃到编号为 b?p (如果 0≤b?p<N)或 b+p (如果 0≤b+p<N)的摩天楼。
编号为 0 的 doge 是所有 doge 的首领,它有一条紧急的消息要尽快传送给编 号为 1 的 doge。任何一个收到消息的 doge 有以下两个选择:
跳跃到其他摩天楼上;
将消息传递给它当前所在的摩天楼上的其他 doge。
请帮助 doge 们计算将消息从 0 号 doge 传递到 1 号 doge 所需要的最少总跳跃步数,或者告诉它们消息永远不可能传递到 1 号 doge。
Input
输入的第一行包含两个整数 N 和 M。
接下来 M 行,每行包含两个整数 Bi 和 Pi。
Output
输出一行,表示所需要的最少步数。如果消息永远无法传递到 1 号 doge,输出 ?1。
Sample Input
5 3
0 2
1 1
4 1
Sample Output
5
explanation
下面是一种步数为 5 的解决方案:
0 号 doge 跳跃到 2 号摩天楼,再跳跃到 4 号摩天楼(2 步)。
0 号 doge 将消息传递给 2 号 doge。
2 号 doge 跳跃到 3 号摩天楼,接着跳跃到 2 号摩天楼,再跳跃到 1 号摩天楼(3 步)。
2 号 doge 将消息传递给 1 号 doge。
HINT
子任务
所有数据都保证 0≤Bi<N。
子任务 1 (10 分)
1≤N≤10
1≤Pi≤10
2≤M≤3
子任务 2 (12 分)
1≤N≤100
1≤Pi≤100
2≤M≤2000
子任务 3 (14 分)
1≤N≤2000
1≤Pi≤2000
2≤M≤2000
子任务 4 (21 分)
1≤N≤2000
1≤Pi≤2000
2≤M≤30000
子任务 5 (43 分)
1≤N≤30000
1≤Pi≤30000
2≤M≤30000
分块思想+SPFA+建图优化
看到题目第一眼肯定会想到最短路,但最坏情况有n^2条边,显然任何一种最短路算法都不能过。
所以我们考虑用分块的思想来优化建图。
- Pi>sqrt(n),暴力加入每一条边,每次最多sqrt(n)条边。
- Pi≤sqrt(n),对于每个点添加sqrt(n)个辅助点,这里可以理解成一栋楼有许多层,每一层一步能走的范围都不同,然后每一层分别连边,每一层到楼底连边。对于一只doge,从楼底到Pi对应的楼层连边。边数是O(n*sqrt(n))的。
综上,总边数和总点数都是O(n*sqrt(n))。
然而这道题还卡内存,所以要用到一个小trick:将块的大小和100取min。至于为什么我也不知道,反正这就对了。
分块思想太机智!!!
#include<iostream> #include<cstdlib> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define maxn 30005*105 #define maxm 30005*500 #define inf 1000000000 using namespace std; int n,m,s,t,tot,tmp; int b[30005],p[30005],head[maxn],dis[maxn]; bool inq[maxn]; struct edge_type{int next,to,v;}e[maxm]; queue<int> q; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void add_edge(int x,int y,int v) { e[++tot]=(edge_type){head[x],y,v};head[x]=tot; } inline void spfa() { memset(dis,-1,sizeof(dis)); dis[s]=0;q.push(s);inq[s]=true; while (!q.empty()) { int x=q.front();q.pop();inq[x]=false; for(int i=head[x];i;i=e[i].next) { int y=e[i].to; if (dis[y]==-1||dis[y]>dis[x]+e[i].v) { dis[y]=dis[x]+e[i].v; if (!inq[y]) q.push(y),inq[y]=true; } } } } inline int num(int x,int y) { return x*n+y; } int main() { n=read();m=read(); F(i,1,m) b[i]=read()+1,p[i]=read(); s=b[1];t=b[2]; tmp=min((int)sqrt(n),100); F(i,1,tmp) F(j,1,n) add_edge(num(i,j),j,0); F(i,1,tmp) F(j,1,n-i) add_edge(num(i,j),num(i,j+i),1),add_edge(num(i,j+i),num(i,j),1); F(i,1,m) { if (p[i]<=tmp) add_edge(b[i],num(p[i],b[i]),0); else { for(int j=1;b[i]+j*p[i]<=n;j++) add_edge(b[i],b[i]+j*p[i],j); for(int j=1;b[i]-j*p[i]>=1;j++) add_edge(b[i],b[i]-j*p[i],j); } } spfa(); printf("%d\n",dis[t]); return 0; }