1 条题解

  • 0
    @ 2026-10-26 2:29:30

    思路与算法

    先考虑顺时针。令gain[i]=p[i]-d[i],把这组净增油量重复两次并建立64位前缀和P。从第s站出发,走完前t条边后的油量为P[s+t]-P[s]。因此一整圈可行,当且仅当P[s+1..s+n]中的最小值不小于P[s]。

    这些查询是连续滑动的长度n窗口,用单调递增前缀下标队列,在O(n)时间内得到每个起点的判定。注意窗口不含出发前的P[s],但包含返回起点时的P[s+n]。

    逆时针从i走向i-1时,消耗的是d[i-1],所以逆向净增为p[i]-d[i-1](下标循环)。按n-1至0反转站序后,再执行相同的滑动窗口判定,并映射回原站编号。任一方向可行就输出TAK,否则NIE。程序使用缓冲读写应对百万站和百万行输出。

    正确性说明

    一次从起点开始的行驶中,每到站先收集该站油量,再消耗对应边距离,故经过t条边后的净油量恰是该段gain之和,也就是前缀差。检查整圈中全部t=1..n的最小差不小于0,等价于全过程从未缺油,含最后返回的那条边。

    单调队列始终保留当前长度n窗口内未被更小、更新的前缀淘汰的候选,队首给出准确窗口最小值。反向数组逐项使用实际反向要走的边,反转后与单方向模型完全一致。最后两个方向取或,正好符合题意的顺时针或逆时针任选一种。

    复杂度

    输入数组、净增数组、结果数组以及每次判定的双倍前缀和、下标队列均初始化O(n)。每个前缀至多入队、出队一次,两个方向总时间仍为O(n),空间O(n)。输入和输出字符总长度也为O(n),缓冲区为固定大小。

    易错点

    逆向用d[i-1],不是d[i];0号站的上一条边为d[n-1]。两个方向满足其一即可,不能要求同时成功。p可以为0,距离d必须为正;候选资料中距离0的点已整点排除。双倍前缀可接近4*10^15,必须使用long long。原站从1编号,代码从0编号,但按原顺序逐行输出即可。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    struct Input {
        static const int S = 1 << 16;
        char data[S];
        int at = 0, len = 0;
        int get() {
            if (at == len) {
                len = fread(data, 1, S, stdin);
                at = 0;
                if (!len)
                    return EOF;
            }
            return data[at++];
        }
        ll next() {
            int c = get();
            while (c <= 32 && c != EOF)
                c = get();
            ll x = 0;
            while (c >= '0' && c <= '9') {
                x = x * 10 + c - '0';
                c = get();
            }
            return x;
        }
    };
    struct Output {
        static const int S = 1 << 16;
        char data[S];
        int at = 0;
        ~Output() {
            flush();
        }
        void flush() {
            fwrite(data, 1, at, stdout);
            at = 0;
        }
        void put(char c) {
            if (at == S)
                flush();
            data[at++] = c;
        }
        void line(bool okay) {
            const char *s = okay ? "TAK\n" : "NIE\n";
            while (*s)
                put(*s++);
        }
    };
    int main() {
        Input in;
        Output out;
        int n = in.next();
        vector<ll> p(n), d(n), gain(n);
        for (int i = 0; i < n; ++i) {
            p[i] = in.next();
            d[i] = in.next();
        }
        vector<unsigned char> okay(n);
        auto check = [&](bool reversed) {
            for (int j = 0; j < n; ++j) {
                int i = reversed ? n - 1 - j : j;
                gain[j] = p[i] - d[reversed ? (i ? i - 1 : n - 1) : i];
            }
            vector<ll> prefix(2 * n + 1);
            for (int i = 1; i <= n; ++i)
                prefix[i] = prefix[i - 1] + gain[i - 1];
            for (int i = 1; i <= n; ++i)
                prefix[n + i] = prefix[n] + prefix[i];
            vector<int> q(2 * n);
            int head = 0, tail = 0;
            for (int i = 1; i < 2 * n; ++i) {
                while (head < tail && q[head] <= i - n)
                    ++head;
                while (head < tail && prefix[q[tail - 1]] >= prefix[i])
                    --tail;
                q[tail++] = i;
                if (i >= n) {
                    int start = i - n, index = reversed ? n - 1 - start : start;
                    if (prefix[q[head]] >= prefix[start])
                        okay[index] = 1;
                }
            }
        };
        check(false);
        check(true);
        for (int i = 0; i < n; ++i)
            out.line(okay[i]);
    }
    
    • 1

    信息

    ID
    1648
    时间
    1000ms
    内存
    512MiB
    难度
    普及+
    标签
    递交数
    1
    已通过
    1
    上传者