P3509 [POI2010] 尺取法 + 倍增

题意

传送门 P3509 [POI2010]ZAB-Frog

题解

首先求解距离各点第 k k k 小的点。由于位置的有序性,距离各点第 1 − k 1-k 1k 小的位置连续分布。考虑从左向右动态地维护距离各点第 1 − k 1-k 1k 小的位置,设这样的区间为 [ l , r ] [l,r] [l,r],当前点为 i i i,设 f ( x ) = ∣ p x − p i ∣ f(x)=\lvert p_x-p_i\rvert f(x)=pxpi,则区间满足 r − l = k , max ⁡ ( f ( l ) , f ( r ) ) ≤ min ⁡ ( f ( l − 1 ) , f ( r + 1 ) ) r-l=k,\max(f(l),f(r))\leq \min(f(l-1),f(r+1)) rl=k,max(f(l),f(r))min(f(l1),f(r+1)) 此时距离 i i i k k k 小的点为 l , r l,r l,r 之一。考虑到有序性,有 { f ( l ) ≤ f ( l − 1 ) f ( r ) ≤ f ( r + 1 ) \begin{cases}f(l)\leq f(l-1)\\ f(r)\leq f(r+1)\\ \end{cases} {f(l)f(l1)f(r)f(r+1) 联立得到 { f ( l ) ≤ f ( r + 1 ) f ( r ) ≤ f ( l − 1 ) \begin{cases}f(l)\leq f(r+1)\\ f(r)\leq f(l-1)\\ \end{cases} {f(l)f(r+1)f(r)f(l1) i i i 增大, f ( r ) ≤ f ( l − 1 ) f(r)\leq f(l-1) f(r)f(l1) 仍然满足;若不满足 f ( l ) ≤ f ( r + 1 ) f(l)\leq f(r+1) f(l)f(r+1),则区间向右移动,直到满足上述性质。点 1 1 1 对应的区间显然是 [ 1 , k + 1 ] [1,k+1] [1,k+1],尺取法求解其余各点。

M M M 二进制拆分,就可以使用倍增的方法, O ( N log ⁡ M ) O(N\log M) O(NlogM) 求解各点的最终位置。若预处理各点移动 2 k 2^k 2k 步的终点,空间复杂度 O ( N log ⁡ M ) O(N\log M) O(NlogM),显然难以胜任。考虑类似快速幂的过程,倍增计算各点移动 2 k 2^k 2k 步的终点,同时求解答案,空间复杂度 O ( N ) O(N) O(N)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000005;
int N, K, F[maxn], G[maxn], res[maxn];
ll M, P[maxn];

inline ll read()
{
    ll x = 0;
    char c = 0;
    for (; c < '0' || c > '9'; c = getchar())
        ;
    for (; c >= '0' && c <= '9'; c = getchar())
        x = (x << 1) + (x << 3) + c - '0';
    return x;
}

int main()
{
    scanf("%d%d%lld", &N, &K, &M);
    for (int i = 1; i <= N; ++i)
        P[i] = read();
    for (int i = 1, l = 1, r = K + 1; i <= N; ++i)
    {
        while (r + 1 <= N && P[r + 1] - P[i] < P[i] - P[l])
            ++l, ++r;
        F[i] = P[r] - P[i] > P[i] - P[l] ? r : l;
    }
    for (int i = 1; i <= N; ++i)
        res[i] = i;
    while (M)
    {
        if (M & 1)
            for (int i = 1; i <= N; ++i)
                res[i] = F[res[i]];
        memcpy(G + 1, F + 1, sizeof(int) * N);
        for (int i = 1; i <= N; ++i)
            F[i] = G[G[i]];
        M >>= 1;
    }
    for (int i = 1; i <= N; ++i)
        printf("%d ", res[i]);
    putchar('\n');
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值