洛谷 P4065 题解

题目链接

模拟赛 T1,纪念一下第一次场切紫。(话说场上这么多人切是不是都找到原了,就我这么傻想了半天)

正难则反,很容易的将题目转化为选择若干种颜色,使这些颜色在原数组中的位置连续。

prei 为颜色 i 最早出现的位置,sufi 为颜色 i 最晚出现的位置。假设通过选择若干颜色得到的位置连续的区间为 [l,r],充要条件为:

i ϵ [l,r] preil

i ϵ [l,r] sufir

此结论可以感性理解,不想证了。

枚举一个右端点,(左端点也可以)发现 sufirr 为一个定值,于是可以先二分出最小的 l,这里还要预处理 ``st 表``。第二个条件 preil 就有点棘手。

其实也很简单,此时就需要用到线段树了,将区间 [prei+1,i] 覆盖为 0 即可,意思就是选了 i 必须要向左延伸到 prei,否则构不成联通块。

然后就是查询,区间为二分出的 l 和当前枚举的右端点 r

本地跑了 700ms,时限 200ms 居然能过就离谱了。

复制代码
#include <bits/stdc++.h>
#define For(i, a, b) for (int i = (a); i <= (b); i++)
#define foR(i, a, b) for (int i = (a); i >= (b); i--)
using namespace std;
int n, x, y;
int aa[19];
int lg[300005], f[19][300005];  // ST 表
int c[300005];                  //颜色
int pre[300005], suf[300005];   //判断一个区间是否合法
int sum[1200005];
bool cover[1200005];  //线段树
inline int read() {
    char ch = getchar();
    int x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - 48;
        ch = getchar();
    }
    return x;
}
int qmax(int x, int y) {
    int l = lg[y - x + 1];
    return max(f[l][x], f[l][y - aa[l] + 1]);
}
void build(int l, int r, int k) {
    if (l == r) {
        sum[k] = 1;
        cover[k] = 0;
        return;
    }
    cover[k] = 0;
    int mid = l + r >> 1;
    build(l, mid, k << 1);
    build(mid + 1, r, k << 1 | 1);
    sum[k] = sum[k << 1] + sum[k << 1 | 1];
}
void pushdown(int l, int r, int k) {
    if (cover[k]) {
        cover[k << 1] = cover[k << 1 | 1] = 1;
        sum[k << 1] = sum[k << 1 | 1] = 0;
    }
}
void update(int l, int r, int k) {  //区间覆盖为 0
    if (x <= l && y >= r) {
        cover[k] = 1;
        sum[k] = 0;
        return;
    }
    pushdown(l, r, k);
    int mid = l + r >> 1;
    if (x <= mid)
        update(l, mid, k << 1);
    if (y > mid)
        update(mid + 1, r, k << 1 | 1);
    sum[k] = sum[k << 1] + sum[k << 1 | 1];
}
int query(int l, int r, int k) {
    if (x <= l && y >= r)
        return sum[k];
    pushdown(l, r, k);
    int mid = l + r >> 1, res = 0;
    if (x <= mid)
        res = query(l, mid, k << 1);
    if (y > mid)
        res += query(mid + 1, r, k << 1 | 1);
    return res;
}
void init() {
    For(i, 0, 18) aa[i] = 1 << i;
    lg[1] = 0;
    For(i, 2, 300000) lg[i] = lg[i / 2] + 1;
}
void solve() {
    n = read();
    build(1, n, 1);
    For(i, 1, n) pre[i] = 0;
    For(i, 1, n) {
        c[i] = read();
        if (!pre[c[i]])
            pre[c[i]] = i;
        suf[c[i]] = i;
    }
    For(i, 1, n) f[0][i] = suf[c[i]];
    For(j, 1, 18) for (int i = 1; i + aa[j] - 1 <= n; i++) f[j][i] =
        max(f[j - 1][i], f[j - 1][i + aa[j - 1]]);
    long long ans = 0;
    For(i, 1, n) {
        int l = i;
        foR(j, 18, 0) if (l > aa[j]) {
            int x = l - aa[j];
            if (qmax(x, i) <= i)
                l = x;
        }
        if (pre[c[i]] != i) {
            x = pre[c[i]] + 1;
            y = i;
            update(1, n, 1);
        }
        if (suf[c[i]] > i)
            continue;
        x = l;
        y = i;
        ans += query(1, n, 1);
    }
    cout << ans;
}
signed main() {
    init();
    int _ = 1;
    _ = read();
    while (_--) {
        solve();
        cout << '\n';
    }
    return 0;
}
复制代码
posted @   Xy_top  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
点击右上角即可分享
微信分享提示