洛谷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 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9683152.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值