倍增

# 跑路

## 题目描述

小 A 的工作不仅繁琐,更有苛刻的规定,要求小 A 每天早上在 6:00 之前到达公司,否则这个月工资清零。可是小 A 偏偏又有赖床的坏毛病。于是为了保住自己的工资,小 A 买了一个空间跑路器,每秒钟可以跑 2k 千米(k 是任意自然数)。当然,这个机器是用 `longint` 存的,所以总跑路长度不能超过 `maxlongint` 千米。小 A 的家到公司的路可以看做一个有向图,小 A 家为点 1,公司为点 n,每条边长度均为一千米。小 A 想每天能醒地尽量晚,所以让你帮他算算,他最少需要几秒才能到公司。数据保证 1n 至少有一条路径。

## 输入格式

第一行两个整数 n,m,表示点的个数和边的个数。

接下来 m 行每行两个数字 u,v,表示一条 uv 的边。

## 输出格式

一行一个数字,表示到公司的最少秒数。

## 样例 #1

### 样例输入 #1

```
4 4
1 1
1 2
2 3
3 4
```

### 样例输出 #1

```
1
```

## 提示

**【样例解释】**

11234,总路径长度为 4 千米,直接使用一次跑路器即可。

**【数据范围】**

50% 的数据满足最优解路径长度 1000

100% 的数据满足 2n50m104,最优解路径长度 `maxlongint`。

 

//将所有的点进行倍增判断,点数不多,以2的64次方为上限,可以这样考虑 //三重循环类似于floyd,然后设置一个中间点 e,如果i能到e,j也能到e,那么i到j就是2的1次方,此时就可以判断为1秒 //然后连图跑最短路即可 #include<bits/stdc++.h> #define int long long using namespace std; const int N=1e4+10,MAXN=100,MAXM=105; int edge[MAXN][MAXN][MAXM]; int e[N],ne[N],h[N],idx; int n,m,res,dist[MAXN]; bool vis[MAXN]; typedef pair<int,int>pii; void add(int a,int b) { e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void dijkstra() { dist[1]=0; priority_queue<pii,vector<pii>,greater<pii>>que; que.push({0,1}); while(!que.empty()){ auto now=que.top(); que.pop(); int dis=now.first,pos=now.second; if(vis[pos]) continue; vis[pos]=true; for(int i=h[pos];~i;i=ne[i]){ int j=e[i]; if(dist[j]>dist[pos]+1){ dist[j]=dist[pos]+1; que.push({dist[j],j}); } } } } signed main() { memset(h,-1,sizeof h); memset(dist,0x3f,sizeof dist); cin>>n>>m; for(int i=1;i<=m;i++){ int u,v; scanf("%lld%lld",&u,&v); edge[u][v][0]=1; } for(int k=1;k<=64;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int e=1;e<=n;e++) if(edge[i][e][k-1]==1&&edge[e][j][k-1]==1) edge[i][j][k]=1; for(int k=0;k<=64;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(edge[i][j][k]) add(i,j); dijkstra(); cout<<dist[n]; return 0; }

 

//国旗计划: //https://www.luogu.com.cn/problem/P4155 #include<bits/stdc++.h> using namespace std; const int N=2e5+10; struct node { int l,r,id; bool operator<(const node&w) const { return l<w.l; } }soldier[2*N]; int n,m,res[N],f[2*N][25]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d%d",&soldier[i].l,&soldier[i].r); if(soldier[i].l>soldier[i].r) soldier[i].r+=m; soldier[i].id=i; } sort(soldier+1,soldier+1+n); for(int i=1;i<=n;i++) soldier[i+n].l=soldier[i].l+m,soldier[i+n].r=soldier[i].r+m,soldier[i+n].id=i; for(int i=1,pos=i;i<=2*n;i++){ while(pos<=2*n&&soldier[pos].l<=soldier[i].r) pos++; //寻找最接近r但不超过的点 f[i][0]=pos-1; } for(int j=1;j<20;j++) for(int i=1;i<=2*n;i++) f[i][j]=f[f[i][j-1]][j-1]; //从i位置跳2^j个位置 for(int i=1;i<=n;i++){ int cnt=1,target=soldier[i].l+m,k=i; //将cnt设置为1是因为,我们在下面枚举步数的时候,要求右端点小于目标值 //由于是小于,不是小于等于,所以肯定还有一个超过它的,即+1,为什么不在搜索的时设置成小于等于呢? //是因为最后一步走的区间右端点可能是正好到target,也可能是大于target //如果设成<=target就不能确定走没走最后一步,<target就一定没走最后一步,可以直接加1 for(int j=19;j>=0;j--) if(f[k][j]!=0&&soldier[f[k][j]].r<target){ cnt+=(1<<j); k=f[k][j]; } res[soldier[i].id]=cnt+1; //加上本来的一个 } for(int i=1;i<=n;i++) printf("%d ",res[i]); return 0; }

 


__EOF__

本文作者Sakurajimamai
本文链接https://www.cnblogs.com/o-Sakurajimamai-o/p/17709077.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   o-Sakurajimamai-o  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
-- --
点击右上角即可分享
微信分享提示