鳝题选做

orz sqc


hdu6701 多校2019Day10K Make Rounddog Happy

题目传送门

https://vjudge.net/problem/HDU-6701

(vjudge 重度依赖,原谅我不尊重原题哈)

题解

我们很轻松地求出一个最大值管辖哪些区域,但是由于“所选区间每个元素要互不相同”这一限制条件的存在,所以不能直接求出。最优的方法需要枚举一个端点,然后通过预处理 \(pre_i\) 表示 \(i\) 位置上的数之前的最后一次出现的位置,那么这个东西的前缀最大值就是可行以 \(i\) 为右端点时的极限左端点。

但是很显然这样做是 \(O(n^2)\) 的。

可以发现,每次如果只处理跨过最大值的区间,然后递归剩下的区间,就可以做出来了。但是每一次需要扫左右两边的其中一个区间。

所以可以用启发式分裂(这样叫是因为最后反过来看这就是一个启发式合并),每次扫描较小区间。


时间复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
    int f = 0, c;
    while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    x = c & 15;
    while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    f ? x = -x : 0;
}

const int N = 300000 + 7;
const int LOG = 19;

int n, k;
ll ans;
int a[N], pre[N], vpre[N], nxt[N], vnxt[N];
pii rmq[N][LOG];

inline void rmq_init() {
    for (int i = 1; i <= n; ++i) rmq[i][0] = pii(a[i], i);
    for (int j = 1; (1 << j) <= n; ++j)
        for (int i = 1; i + (1 << j) - 1 <= n; ++i)
            rmq[i][j] = std::max(rmq[i][j - 1], rmq[i + (1 << (j - 1))][j - 1]);
}
inline pii qmax(int l, int r) {
    int k = std::__lg(r - l + 1);
    return std::max(rmq[l][k], rmq[r - (1 << k) + 1][k]);
}

inline void solve(int l, int r) {
    if (l > r) return;
    int mid = qmax(l, r).se;
    if (mid - l <= r - mid) {
        int mn, nd = a[mid] - k;
        for (int i = mid; i >= l; --i) {
            mn = std::min(nxt[i], r);
            if (mn < mid) break;
            if (mn - i + 1 >= nd) ans += mn - std::max(mid, i + nd - 1) + 1;
        }
    } else {
        int mx = 0, nd = a[mid] - k;
        for (int i = mid; i <= r; ++i) {
            mx = std::max(pre[i], l);
            if (mx > mid) break;
            if (i - mx + 1 >= nd) ans += std::min(mid, i - nd + 1) - mx + 1;
        }
    }
    solve(l, mid - 1), solve(mid + 1, r);
}

inline void work() {
    rmq_init();
    ans = 0;
    solve(1, n);
    printf("%lld\n", ans);
}

inline void init() {
    read(n), read(k);
    memset(vpre, 0, sizeof(int) * (n + 1));
    for (int i = 1; i <= n; ++i) {
        read(a[i]);
        pre[i] = std::max(pre[i - 1], vpre[a[i]] + 1);
        vpre[a[i]] = i;
    }
    for (int i = 1; i <= n; ++i) vnxt[i] = n + 1;
    nxt[n + 1] = n + 1;
    for (int i = n; i; --i) nxt[i] = std::min(nxt[i + 1], vnxt[a[i]] - 1), vnxt[a[i]] = i;
}

int main() {
#ifdef hzhkk
    freopen("hkk.in", "r", stdin);
#endif
    int T;
    read(T);
    while (T--) {
        init();
        work();
    }
    fclose(stdin), fclose(stdout);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hankeke/p/sqc_noip2019.html