洛谷 CF555D 最高机密

1 CF555D 最高机密

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

Andrewid the Android is a galaxy-famous detective. Now he is busy with a top secret case, the details of which are not subject to disclosure.

However, he needs help conducting one of the investigative experiment. There are n pegs put on a plane, they are numbered from \(1\) to \(n\), the coordinates of the \(i-th\) of them are \((x_i, 0)\). Then, we tie to the bottom of one of the pegs a weight on a tight rope of length \(l\) (thus, its coordinates will be equal to \((x_i, -l)\), where \(i\) is the number of the used peg). Then the weight is pushed to the right, so that it starts to rotate counterclockwise. At the same time, if the weight during rotation touches some of the other pegs, it then begins to rotate around that peg. Suppose that each peg itself is very thin and does not affect the rope length while weight is rotating around it.

More formally, if at some moment the segment of the rope contains one or more pegs in addition to the peg around which the weight is rotating, the weight will then rotate around the farthermost one of them on a shorter segment of a rope. In particular, if the segment of the rope touches some peg by its endpoint, it is considered that the weight starts to rotate around that peg on a segment of the rope of length \(0\).

At some moment the weight will begin to rotate around some peg, without affecting the rest of the pegs. Andrewid interested in determining the number of this peg.

Andrewid prepared m queries containing initial conditions for pushing the weight, help him to determine for each of them, around what peg the weight will eventually rotate.

数据范围:\(1 ≤ n, m ≤ 2\times 105;\space - 10^9 ≤ x_i ≤ 10^9\)

3 题解

看到题,很容易可以想到二分算法:用于计算当前绳子最远能碰到的钉子的位置。显而易见的是,我们每次都换一个方向左右寻找目标点,找到后就将绳子长度减少,并换一个方向,直到最远的能碰到的钉子就是当前的钉子。

显然,这个算法的时间复杂度很高,无法通过本题,因此我们考虑优化。

我们可以发现,如果绳子过长,会绕某几个钉子很多圈。这里我们就没必要一圈一圈地绕了:显然,对于某一固定长度 \(L\) 和绕的圈的长度 \(r\),绳子最终的长度就是 \(L\mod r\)。而我们只需要看 \(\lfloor \dfrac{L}{r} \rfloor\) 的奇偶性就可以判断当我们跳出当前的绕圈后的朝向了。

这种做法的时间复杂度其实是有保障的:每一次取模后,\(L\) 的值都会小于等于 \(\lfloor \dfrac{L}{2} \rfloor\)。这是因为,如果 \(r \le \lfloor \dfrac{L}{2} \rfloor\),那么此时取模后 \(L\) 一定小于一半;如果 \(r > \lfloor \dfrac{L}{2} \rfloor\),那么取模时我们一定可以从 \(L\) 中拿出一个 \(r\),剩余的部分一定小于等于 \(\lfloor \dfrac{L}{2} \rfloor\),所以剩余的部分就是余数,且小于等于 \(\lfloor \dfrac{L}{2} \rfloor\)。而这个定理告诉我们,每次取模后 \(L\) 都会至少减少一半,那么经过 \(log_2L\) 次取模,\(L\) 就大约变成 \(1\),最终碰不到其他钉子了。因此,这个算法的时间复杂度大概就是 \(O(mlog_2^2L)\),由于实际上的 \(L\) 可能远远没有经过 \(log_2L\) 次就变成碰不到其他钉子的长度了,并且这道题给开了两秒,所以是可以通过的。

4 代码(空格警告):

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5+10;
int n, m, a, L, l, r, mid, ans, op, nxt, ori;
int cha[N], to[N];
struct node
{
    int ori, num;
}x[N];
int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return x * f;
}
bool cmp(node x, node y)
{
    return x.num < y.num;
}
int find(int pos, int len, int f)
{
    if (f)
    {
        l = pos;
        r = n;
        mid = (l + r) >> 1;
        while (l <= r)
        {
            if (x[mid].num - x[pos].num <= len) ans = mid, l = mid + 1;
            else r = mid - 1;
            mid = (l + r) >> 1;
        }
        if (ans == pos)
        {
            l = 1;
            r = pos;
            mid = (l + r) >> 1;
            while (l <= r)
            {
                if (x[pos].num - x[mid].num <= len) ans = mid, r = mid - 1;
                else l = mid + 1;
                mid = (l + r) >> 1;
            }
        }
    }
    else
    {
        l = 1;
        r = pos;
        mid = (l + r) >> 1;
        while (l <= r)
        {
            if (x[pos].num - x[mid].num <= len) ans = mid, r = mid - 1;
            else l = mid + 1;
            mid = (l + r) >> 1;
        }
    }
    if (ans == pos) return -1;
    return ans;
}
int main()
{
    n = read(), m = read();
    for (int i = 1; i <= n; i++) x[i].num = read(), x[i].ori = i;
    sort(x+1, x+n+1, cmp);
    for (int i = 1; i <= n; i++) to[x[i].ori] = i;
    for (int i = 1; i <= m; i++)
    {
        a = read();
        L = read();
        a = to[a];
        op = 1;
        nxt = find(a, L, op);
        while (nxt != -1)
        {
            if (nxt < a)
            {
                op = 1;
                L -= (x[a].num - x[nxt].num);
                a = nxt;
                nxt = find(a, L, op);
                if (nxt == -1) break;
                if ((L / (x[nxt].num - x[a].num)) % 2)
                {
                    op = 0;
                    L %= (x[nxt].num - x[a].num);
                    a = nxt;
                }
                else
                {
                    op = 1;
                    L %= (x[nxt].num - x[a].num);
                }
            }
            else if (nxt > a)
            {
                op = 0;
                L -= (x[nxt].num - x[a].num);
                a = nxt;
                nxt = find(a, L, op);
                if (nxt == -1) break;
                if ((L / (x[a].num - x[nxt].num)) % 2)
                {
                    op = 1;
                    L %= (x[a].num - x[nxt].num);
                    a = nxt;
                }
                else
                {
                    op = 0;
                    L %= (x[a].num - x[nxt].num);
                }
            }
            nxt = find(a, L, op);
        }
        printf("%d\n", x[a].ori);
    }
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-07-10 21:53  David24  阅读(68)  评论(0编辑  收藏  举报