首页 > 其他分享 >洛谷 P4065 题解

洛谷 P4065 题解

时间:2024-02-16 20:33:06浏览次数:27  
标签:pre suf ch 洛谷 int 题解 mid P4065 300005

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

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

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

$\forall i$ $\epsilon$ $[l,r]$ $pre_i \geq l$

$\forall i$ $\epsilon$ $[l,r]$ $suf_i \leq r$

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

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

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

然后就是查询,区间为二分出的 $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;
}

标签:pre,suf,ch,洛谷,int,题解,mid,P4065,300005
From: https://www.cnblogs.com/Xy-top/p/18017451

相关文章

  • P10149 [Ynoi1999] XM66F题解
    题解首先,问题是静态的区间查询问题,一眼莫队。那么我们就需要考虑所需要维护的内容在区间扩增或者缩减时的变化如何快速维护。我们可以先写出对于区间\([l,r]\)来说,满足\(l\lei<j<k\ler\)的有序三元组\((i,j,k)\)数量的表达式,方便拆式子:\[\sum\limits_{i=l}^{r}......
  • 启动vue-element-admin遇到问题解决方案
    概述从https://github.com/PanJiaChen/vue-element-admin下载代码,按照文档执行,期间遇到一些列问题。1#clonetheproject2gitclonehttps://github.com/PanJiaChen/vue-element-admin.git34#entertheprojectdirectory5cdvue-element-admin67#insta......
  • 出现8080端口占用问题解决
    查到占用端口号并关闭netstat-aon|findstr8080出现:TCP0.0.0.0:80000.0.0.0:0LISTENING23296TCP[::]:8000[::]:0LISTENING23296tasklist|findstr"23296"出现:java.exe......
  • CF739A Alyona and mex 题解
    题目简单构造,首先我们知道一个区间\([l,r]\)内的最大答案为为这个区间的长度\(len\),因为其中可以包括\([0,r-l+1]\)这些数。所以\(ans=min(len_i)\)。考虑如何满足这个条件,设最小长度为\(len_{min}\),我们可以轮流输出\([0,len_{min}]\),因为\(len_{min}\)是最小长度,所......
  • 树状数组-三色二叉树 题解
    题目在这里————————————————————————————————三色二叉树首先题面写的很清楚了是一道树状数组题因为这题的输入方式很特别按二叉树序列所以在输入上要特殊处理如下voidread(intx){//读入+存图以左右子树为形式如l[x]=y即y为x左子树......
  • [Vijos P1448] 校门外的树 题解
    题目描述校门外有很多树,学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两种操作:k==1,读入l,r表示在l到r之间种上一种树,每次操作种的树的种类都不同;k==2,读入l,r表示询问l到r之间有多少种树。注意:每个位置都可以重复种树。......
  • CF55D Beautiful numbers 题解
    题目链接:CF或者洛谷常见知识点组合经典题。首先,一眼数位dp类型题,考虑需要处理些怎样的判断合法数位信息。经典操作对于跟整除有关的判断,数位dp为了减少使用空间,都可以考虑记忆化模数减少空间开销。对于整除若干个数,即整除这若干个数的最小公倍数即可,是一个非常常用......
  • 【题解】P4722 【模板】最大流 加强版 / 预流推进
    更好阅读体验CHAPTER0废话1.常见的最大流算法可以大致分为两种:找增广路和预流推进2.从理论时间复杂度上界来看,找增广路算法的最坏时间复杂度为\(O(n^2m)\),而预流推进算法的最坏时间复杂度为\(O(n^2\sqrt{m})\),看起来预流推进要显然优于找增广路,但在实际应用(尤指OI)中,由于包......
  • HDU-Employment Planning题解
    题目在这里————————————————————————————————EmploymentPlanning简单的一道dp关键的点在于想到用枚举实现各种情况的讨论关键的注释写在代码里了还是很清晰的捏~#include<bits/stdc++.h>#definefo(x,y,z)for(int(x)=(y);(x)<=(z);(x......
  • 题解 LGP10144【[WC/CTS2024] 水镜】
    题解P10144【[WC/CTS2024]水镜】题目描述给定一个长度为\(n\)的正整数序列\(h_1,h_2,\cdots,h_n\),求满足以下所有条件的二元组\((u,v)\)的数量:\(1\leu<v\len\),且\(u,v\)为整数;存在一个正实数\(L\)以及一个长度为\((v-u+1)\)的序列\(r_u,r_{u+......