题目链接:Codeforces 1148E

数轴上有 $n$ 块石头。最初,第 $i$ 个石头位于坐标 $s_i$ 的位置。同一个地方可能有不止一块石头。

你可以进行如下操作任意次(可以为 $0$ 次):

  • 拿出下标为 $i, j$ 且满足 $s_i \le s_j$ 的两块石头,选择一个整数 $d$ 满足 $0 \le 2 \cdot d \le s_j - s_i$ 并将第 $i$ 块石头放到坐标为 $(s_i + d)$ 的地方,将第 $j$ 块石头放到坐标为 $(s_j - d)$ 的地方。换言之,将两块石头相互靠近。

你想通过移动,将石头的坐标变为 $t_1, t_2, \dots, t_n$,注意石头的顺序是无关紧要的。

判断是否存在一种移动石头的方法。如果可以,输出 YES 并构造一种方法;否则输出 NO。你不需要最小化移动次数。

数据范围:$1 \le n \le 3 \times 10 ^ 5$,$1 \le s_i, t_i \le 10 ^ 9$。


Solution

注意到顺序是没有用的,而石头之间的相对顺序是不会改变的,我们将 $s, t$ 都按照值从小到大排序。

我们记 $\delta_i = s_i - t_i$,由于石头是不断靠近的,那么 $\delta$ 的某个前缀一定满足 $\forall i, \delta_i < 0$;同理某个后缀一定满足 $\forall i, \delta_i > 0$;而 $\delta_i = 0$ 的那些位置是没有用的,因为它们已经在正确的坐标了。

我们从前往后扫,一旦遇到 $\delta_i > 0$ 就用之前的某个 $\delta_j < 0$ 去抵消。拿个队列、栈之类的乱搞。注意在抵消过程中判断 NO 的情况。

时间复杂度:$\mathcal O(n \log n)$。


Code

#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <tuple>

#define fail return printf("NO\n"), 0

const int N = 3e5 + 5;

int n, c[N];
std::pair<int, int> s[N], t[N];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &s[i].first);
        s[i].second = i;
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", &t[i].first);
        t[i].second = i;
    }
    std::sort(s + 1, s + n + 1);
    std::sort(t + 1, t + n + 1);
    std::queue<std::pair<int, int>> q;
    std::vector<std::tuple<int, int, int>> ans;
    for (int i = 1; i <= n; i++) {
        int x = s[i].first - t[i].first, j = s[i].second;
        if (x < 0) {
            q.push(std::make_pair(x, j));
        }
        if (x > 0) {
            while (x && !q.empty()) {
                int y = q.front().first, k = q.front().second;
                q.pop();
                int d = std::min(x, -y);
                x -= d, y += d;
                if (y) q.push(std::make_pair(y, k));
                ans.emplace_back(k, j, d);
            }
            if (x) fail;
        }
    }
    if (!q.empty()) fail;
    printf("YES\n");
    printf("%d\n", (int)ans.size());
    for (auto x : ans) {
        printf("%d %d %d\n", std::get<0>(x), std::get<1>(x), std::get<2>(x));
    }
    return 0;
}
最后修改:2019 年 06 月 03 日
如果觉得我的文章对你有用,请随意赞赏