数轴上有n个点按点权大小升序排列,每个点上有一只青蛙,各自独立行动。每一步都会跳到距离他第k近的点(,距离等于点权之差的绝对值,如果存在两个这样的点,就跳到下标较小的那个), 问每只青蛙跳m步之后到达的位置。
(1<=n<=1e6,1<=m<=1e18)
首先我们通过尺取法预处理出每只青蛙跳一步到达的位置。
然后倍增即可。
to[i][j]=to[to[i][j-1]][j-1]
枚举m的二进制的每一位,每次跳2^i步。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
while(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
while(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,k;
ll m;
ll p[maxn];
ll a[maxn][65];//i跳1步
ll ans[maxn];
int main()
{
cin>>n>>k>>m;
for(int i=1;i<=n;i++) scanf("%lld",p+i);
a[1][0]=k+1;
int l=1,r=k+1;//双指针 固定距离 k+1 其中k个是距离某个数前k小
for(int i=2;i<=n;i++)
{
while(r+1<=n && p[i]-p[l] > p[r+1]-p[i]) l++,r++;
if(p[i]-p[l] >= p[r]-p[i]) a[i][0]=l;
else a[i][0]=r;
}
for(int i=1;i<=64;i++)
{
for(int j=1;j<=n;j++)
a[j][i]=a[a[j][i-1]][i-1];
}
for(int i=1;i<=n;i++) ans[i]=i;
for(int i=0;(1ll<<i)<=m;i++)//跳2^i步
{
if((m>>i)&1) //每只青蛙从ans[j]的位置出发 跳2^i步
{
for(int j=1;j<=n;j++) ans[j]=a[ans[j]][i];
}
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
当然倍增可以用滚动数组优化空间,
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
while(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
while(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,k;
ll m;
ll p[maxn];
ll a[maxn];//i跳1步 其实就是维护连续区间内距离某个数前k小
//跳2^j步到达的位置 滚动数组
ll ans[maxn];
ll f[maxn];
int main()
{
cin>>n>>k>>m;
for(int i=1;i<=n;i++) scanf("%lld",p+i);
a[1]=k+1;
int l=1,r=k+1;//双指针 固定距离 k+1 其中k个是距离某个数前k小
for(int i=2;i<=n;i++)
{
while(r+1<=n && p[i]-p[l] > p[r+1]-p[i]) l++,r++;
if(p[i]-p[l] >= p[r]-p[i]) a[i]=l;
else a[i]=r;
}
for(int i=1;i<=n;i++) ans[i]=i;
for(int i=0;(1ll<<i)<=m;i++)//跳2^i步
{
if((m>>i)&1)
{
for(int j=1;j<=n;j++) ans[j]=a[ans[j]];
}
//a[i]是从i位置出发 跳2^x步 保存给f[i]
for(int i=1;i<=n;i++) f[i]=a[i];
//f[f[i]]就是从i出发 先跳2^x步,再跳2^x步
for(int i=1;i<=n;i++) a[i]=f[f[i]];
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}