专栏文章
题解:CF207B3 Military Trainings
CF207B3题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miozshrf
- 此快照首次捕获于
- 2025/12/03 03:49 3 个月前
- 此快照最后确认于
- 2025/12/03 03:49 3 个月前
题目传送门
题目大意
每个坦克有一个接收半径 ,每次要从最前面的坦克把信号传给最后一个,最后一个在跑到前面来,重复执行,每两个坦克之间传输信号所需时间为 ,求总的最小时间。
思路
考虑 的情况,我们可以使用模拟的方法。首先破环成链,把原序列复制一份接到序列后面,从 的位置开始枚举,枚举到 ,每次都模拟往前跳,找到能接收的范围内的接收半径最大的坦克,跳到他这里,直到跳到发送信号的坦克的位置。
时间复杂度 。
考虑 的情况,在先前的暴力的基础上,我们可以用线段树或者
st 表预处理出,每一个坦克 在 范围中能找到最大接收半径的坦克编号,然后每次跳的时候,直接跳到这个范围内的最大接收半径的坦克即可。这样的话就可以做到在模拟的时候做到 。最终时间复杂度为
考虑 的情况,注意到跳跃可以用倍增处理, 表示第 个坦克跳 次能跳到的坦克编号,那么每次都可以跳 次,大大缩减了跳跃的时间,做到 的时间来模拟跳跃。
注意,每次要跳到正好差一次跳到到发送信号的坦克的位置,而且最后到达的位置 不是能跳一步就跳到目标位置的,因为是 的下一次跳跃的位置上的坦克的接收半径包含了目标位置,所以要跳两次。
代码
CPP#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=501010;
ll n,a[N],ans,t[N],fa[N][30],st[N][30];
void init(){
for(ll i=1;i<=2*n;i++)st[i][0]=i;
for(ll j=1;j<=29;j++){
for(ll i=1;i<=2*n-(1<<j)+1;i++){
if(a[st[i][j-1]]<a[st[i+(1<<(j-1))][j-1]])st[i][j]=st[i][j-1];
else st[i][j]=st[i+(1<<(j-1))][j-1];
}
}
}
ll query(ll l,ll r){
ll k=log2(r-l+1);
if(a[st[l][k]]<a[st[r-(1<<k)+1][k]])return st[l][k];
else return st[r-(1<<k)+1][k];
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
}
for(int i=1;i<=2*n;i++)a[i]=max(i-a[i],1ll);
init();
for(int i=1;i<=2*n;i++){
fa[i][0]=query(a[i],i);
}
for(int j=1;j<=29;j++){
for(int i=1;i<=2*n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
for(int i=1;i<=n;i++){
ll cnt=2;
ll now=i+n-1;
if(a[now]<=i){
ans++;
continue;
}
for(ll j=29;j>=0;j--){
if(a[fa[now][j]]>i)cnt+=(1<<j),now=fa[now][j];
}
ans+=cnt;
}
cout<<ans;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...