卧薪尝胆,厚积薄发。
POI2010 Frog
Date: Mon Sep 03 15:42:57 CST 2018 In Category: NoCategory

Description:

一条河上有 $n$ 个石块,每次跳到距这块石头第 $k$ 远的石头上去,问在每块石头上跳 $m$ 次跳到的最终位置。
$1\le n,k \le 10^6,1\le m \le 10^{18}$

Solution:

首先如果找到了第 $k$ 远的石头,那么可以像快速幂一样拆开二进制位倍增。
求第 $k$ 远的石头可以用队列,先把前 $k+1$ 个放进队列,然后每次调整左右端点使得队列中始终为距第 $i$ 个点最近的 $k+1$ 个点(包括第 $k$ 个),左右端点的变化显然是单调的,只要弹队头压队尾即可,第 $k$ 个就是队头队尾距离远的那个。
时间复杂度 $O(n+n\log m)$

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
int n,k;
typedef long long ll;
ll m;
#define MAXN 1000010
ll a[MAXN];
int f[MAXN],tmp[MAXN];
int q[MAXN],head,tail;
int pos[MAXN];
int main()
{
scanf("%d%d%lld",&n,&k,&m);++k;
for(int i = 1;i <= n;++i)scanf("%lld",&a[i]);
int cur;
head = 1;tail = 0;
for(cur = 1;cur <= min(n,k);++cur)q[++tail] = cur;
for(int i = 1;i <= n;++i)
{
while(cur <= n && abs(a[cur] - a[i]) < abs(a[i] - a[q[head]]))
{
++head;
q[++tail] = cur++;
}
if(abs(a[q[head]] - a[i]) >= abs(a[q[tail]] - a[i]))f[i] = q[head];
else f[i] = q[tail];
}
for(int i = 1;i <= n;++i)pos[i] = i;
while(m > 0)
{
if(m & 1)
{
for(int i = 1;i <= n;++i)
{
pos[i] = f[pos[i]];
}
}
for(int i = 1;i <= n;++i)tmp[i] = f[i];
for(int i = 1;i <= n;++i)f[i] = tmp[f[i]];
m = m >> 1;
}
for(int i = 1;i <= n;++i)printf("%d ",pos[i]);
puts("");
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡