TIOJ 1841

好.傳囉! Nice Boat!

Description

給定一個長度 $N$ 的整數序列 $A_i$。
如果你可以找出一個區間,他的前綴和每個數字都大於等於0,而且他的後綴和的每個數字也都大於等於0,我們就稱他是安全區間,請你找出最長的安全區間長度是多少。

Solution

考慮原序列的前綴和 $p_i = A_1+A_2+\cdots+A_i$,$p_0=0$,一個區間 $(l, r]$ 是安全區間若且唯若:

$$
\begin{cases}
\forall l \leq x < r ,& p_r - p_x \geq 0 \\
\forall l < x \leq r ,& p_x - p_l \geq 0 \\
\end{cases}
$$

也就是說,$p_r$ 和 $p_l$ 分別必須要是這段區間的最大值與最小值。

可以用單調隊列對於每個 $r$ 求出它左邊第一個嚴格比 $p_r$ 大的數字 $left_r$,以及對於每個 $l$ 求出它右邊第一個嚴格比 $p_l$ 小的數字 $right_l$。
那麼,安全區間的定義又可以寫成

$$
left_r < l \leq r < right_l
$$

對 $l$ 做掃描線,$l$ 從 $0$ 到 $n$ 每次把符合 $left_r < l$ 的 $r$ 丟進去一個 set 裡面,然後查詢小於 $right_l$ 的 $r$ 最大是多少。這樣時間複雜度 $O(N\log N)$。

不過這題空間限制很緊,無情吃 RE 之後改用 BIT 維護再加上輸入優化才 AC。

AC code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// An AC a day keeps the doctor away.
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#ifdef local
#define safe \
    std::cerr << __PRETTY_FUNCTION__ << " line " << __LINE__ << " safe\n"
#define debug(args...) qqbx(#args, args)
#define orange(args...) danb(#args, args)
using std::cerr;
template <typename... T>
void qqbx(const char *s, T... args) {
    int cnt = sizeof...(T);
    ((cerr << "\e[1;32m(" << s << ") = ("), ...,
     (cerr << args << (--cnt ? ", " : ")\e[0m\n")));
}
template <typename T>
void danb(const char *s, T L, T R) {
    cerr << "\e[1;32m[ " << s << " ] = [ ";
    for (int f = 0; L != R; ++L) cerr << (f++ ? ", " : "") << *L;
    cerr << " ]\e[0m\n";
}
#else
#define safe ((void)0)
#define debug(...) ((void)0)
#define orange(...) ((void)0)
#endif  // local
#define all(v) begin(v), end(v)

using namespace std;
const int inf = 1e9;

template <typename U, typename V>
ostream &operator<<(ostream &o, pair<U,V> p) {
    return o << '(' << p.first << ',' << p.second << ')';
}

inline char readchar() {
    constexpr int B = 1 << 20;
    static char buf[B], *p, *q;
    if (p == q && (q = (p = buf) + fread(buf, 1, B, stdin)) == buf)
        return EOF;
    return *p++;
}
inline int nextint() {
    int x = 0, c = readchar(), f = false;
    while (c < '0' && c != '-') c = readchar();
    if (c == '-') f = true, c = readchar();
    while (c >= '0') x = x * 10 + (c ^ '0'), c = readchar();
    return f ? -x : x;
}

inline void readline(char* s) {
    char c = readchar();
    while (c != '\n') *s++ = c, c = readchar();
}

struct FastOut {
    constexpr static int B = 1 << 20;
    static char buf[B];
    int q;
    inline void writeint(int x, char sep = '\n') {
        static char stk[20];
        char p = 0;
        if (!x) stk[p++] = '0';
        while (x) stk[p++] = x % 10 ^ '0', x /= 10;
        while (p) buf[q++] = stk[--p];
        buf[q++] = sep;
        if (q + 20 >= B) fwrite(buf, 1, q, stdout), q = 0;
    }
    ~FastOut() { fwrite(buf, 1, q, stdout); }
} ouf;
char FastOut::buf[B];

const int maxn = 1000025;
int64_t a[maxn];
int R[maxn];
pair<int,int> evt[maxn];
int stk[maxn];
int sz;
void Add(int x) {
    for (++x; x <= sz; x += x & -x)
        a[x] += 1;
}
int Query(int p) {
    int sum = 0;
    for (++p; p > 0; p -= p & -p)
        sum += a[p];
    int res = 0;
    for (int s = 1<<20; s; s >>= 1)
        if (res + s <= sz && sum > a[res + s])
            sum -= a[res += s];
    return res;
}
signed main() {
    // ios_base::sync_with_stdio(false);
    // cin.tie(nullptr);
    int T = nextint();
    // const int64_t C = 1e9;
    for (int tc = 0; tc < T; tc++) {
        int n = nextint();
        for (int i = 1; i <= n; i++)
            a[i] = nextint();
        for (int i = 1; i <= n; i++)
            a[i] += a[i-1];
        int m = 0;
        {
            int p = 0;
            for (int i = 0; i <= n; i++) {
                while (p && a[stk[p-1]] <= a[i])
                    --p;
                int l = !p ? -1 : stk[p-1];
                stk[p++] = i;
                evt[m++] = { l, i };
            }
        }
        {
            int p = 0;
            for (int i = n; i >= 0; i--) {
                while (p && a[stk[p-1]] >= a[i])
                    --p;
                R[i] = !p ? n+1 : stk[p-1];
                stk[p++] = i;
            }
        }

        sort(evt, evt+m);
        // use a as an BIT
        sz = n+2;
        for (int i = 0; i <= sz; i++)
            a[i] = 0;
        int ans = 0;
        int it = 0;
        for (int i = 0; i <= n; i++) {
            while (it < m && evt[it].first < i) {
                int j = evt[it++].second;
                Add(j);
            }
            int len = Query(R[i]) - i;
            ans = max(ans, len);
        }
        ouf.writeint(ans);
    }
}

至於怎麼用假解拿到 topcoder 又是另一個故事了…應該很快會更新測資(

comments powered by Disqus