首页 > 其他分享 >P5324 题解

P5324 题解

时间:2024-07-02 17:31:10浏览次数:18  
标签:cnt P5324 答案 int 题解 le tag &&

题意

给定一个数列 \(\{a_n\}\),定义一次删除操作为:假设当前序列长度为 \(len\),删除序列中所有等于 \(len\) 的数。

现在有 \(m\) 个操作,每次操作为单点修改或整体加减。每次操作完后,你需要修改若干个数,使得序列能够在若干次删除操作后被删空,求最小修改次数。

数据范围:\(1 \le n, m \le 1.5 \cdot 10^5, 0 \le p \le n, 1 \le x, a_i \le n\) 。

思路

这种操作题肯定是先找出一种静态求取答案的方法,然后再用数据结构等东西维护这个答案。

那么如何求取答案呢?首先按照题意,序列里面不同元素的顺序是无关紧要的,我们只需要看每个数的出现次数即可。而知道了这一性质后该怎么办呢,蒟蒻开始罚坐。。。太菜了 QAQ 其实这是一道结论题,有结论:

假设元素 \(i\) 的出现次数为 \(cnt_i\),将区间 \([i - cnt_i + 1, i]\) 覆盖,答案为 \([1, n]\) 中未被覆盖的点的数量。

考虑证明:

  • 这一定是答案的上界,有一种构造方法是:把一些被覆盖多次的点搬到未被覆盖的位置上,所以答案一定 $\le $ 这种构造方式。
  • 这一定是答案的下界,因为一个点未被覆盖,说明比它大的点到不了它,这样就无法到达比它小的点,故不应存在未被覆盖的点。

于是就可以维护了,对于单点修改操作,很显然。对于整体加减操作,相当于平移 \([1, n]\) 的区间,当 \(x\) 被移出区间时,对 \([x - cnt_x + 1, x]\) 进行区间减一,加进来时区间加一即可。可以用线段树维护最小值和最小值出现次数,时间复杂度 \(O(n \log n)\)。代码稍微有点细节:

#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define per(i, r, l) for (int i = r; i >= l; --i)
#define re(i, n) for (int i = 0; i < n; ++i)
#define pe(i, n) for (int i = n - 1; i >= 0; --i)
using namespace std;
using i64 = long long;
const int N = 9E5 + 5;
int n, m, a[N], L = 3E5;
int cnt[N];
struct segt {
    int tag[N << 2];
    struct node {
        int mn = 1E9;
        int cnt = 0;
    } t[N << 2];
    friend node operator+(node a, node b) {
        node c;
        c.mn = min(a.mn, b.mn);
        if (a.mn == c.mn) c.cnt += a.cnt;
        if (b.mn == c.mn) c.cnt += b.cnt;
        return c;
    }
    void pull(int x) {t[x] = t[x * 2] + t[x * 2 + 1];}
    void build(int x, int l, int r) {
        tag[x] = 0;
        if (l == r) {t[x].cnt = 1; t[x].mn = 0; return ;}
        int mid = (l + r) / 2;
        build(x * 2, l, mid);
        build(x * 2 + 1, mid + 1, r);
        pull(x);
    }
    void apply(int x, int val) {tag[x] += val; t[x].mn += val;}
    void push(int x) {
        apply(x * 2, tag[x]);
        apply(x * 2 + 1, tag[x]);
        tag[x] = 0;
    }
    void modify(int x, int l, int r, int L, int R, int val) {
        if (r < L || l > R) return ;
        if (l >= L && r <= R) return apply(x, val);
        int mid = (l + r) / 2; push(x);
        modify(x * 2, l, mid, L, R, val);
        modify(x * 2 + 1, mid + 1, r, L, R, val);
        pull(x);
    }
    void modify(int l, int r, int val) {
        modify(1, 1, 3 * L, l, r, val);
    }
    void modify(int pos, int val) {modify(pos, pos, val);}
    node query(int x, int l, int r, int L, int R) {
        if (r < L || l > R) return {};
        if (l >= L && r <= R) return t[x];
        int mid = (l + r) / 2; push(x);
        return query(x * 2, l, mid, L, R) + query(x * 2 + 1, mid + 1, r, L, R);
    }
    int query(int l, int r) {
        auto temp = query(1, 1, 3 * L, l, r);
        if (temp.mn != 0) return 0;
        return temp.cnt;
    }
} t;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> m;
    t.build(1, 1, 3 * L);
    rep(i, 1, n) cin >> a[i], ++cnt[a[i] + L];
    rep(i, L + 1, L + n) if (cnt[i]) t.modify(i - cnt[i] + 1, i, 1);
    int tag = 0; while (m--) {
        int p, x; cin >> p >> x;
        if (p) {
            if (a[p] >= 1 - tag && a[p] <= n - tag)
                t.modify(a[p] + L - cnt[a[p] + L] + 1, -1);
            --cnt[a[p] + L];
            a[p] = x - tag;
            if (a[p] >= 1 - tag && a[p] <= n - tag)
                t.modify(a[p] + L - cnt[a[p] + L], 1);
            ++cnt[a[p] + L];
        } else {
            #define modi(pos, val) t.modify(L + pos - cnt[pos + L] + 1, L + pos, val) 
            if (x > 0 && cnt[n - tag + L]) modi((n - tag), -1);
            if (x < 0 && cnt[1 - tag + L]) modi((1 - tag), -1);
            tag += x;
            if (x > 0 && cnt[1 - tag + L]) modi((1 - tag), 1);
            if (x < 0 && cnt[n - tag + L]) modi((n - tag), 1);
        }
        cout << t.query(L + 1 - tag, L + n - tag) << '\n';
    }
}

这道题启发我们,做结论题时,可以多考虑答案的下界和上界都取在哪些值上,从而发现关键结论或方法。

标签:cnt,P5324,答案,int,题解,le,tag,&&
From: https://www.cnblogs.com/CTHOOH/p/18280210

相关文章

  • 复旦大学2023--2024学年第二学期(23级)高等代数II期末考试第七大题解答
    七、(10分) 设$V$是$n$阶实矩阵全体构成的实线性空间, $A$是$n$阶正定实对称阵.对任意的$X,Y\inV$,定义二元函数$(X,Y)=\mathrm{tr}(XAY')$.(1)求证:$(-,-)$是$V$上的一个内积.(2)在上述内积下,$V$成为一个欧氏空间. 设$P,Q\inV$,$V$上的线性算子$......
  • abc360 E 题解
     E对于位置2~n,它们的概率是相等的。n*n个(x,y)对。其中x可以等于y。 对于x/y,y的逆元rev(y)为mul(y,mod-2)。加、减、乘、除都可以做。比如48/9和16/3的结果是一样的,48*rev(9)%mod=16*rev(3)%mod。比如3*rev(2)%mod=(rev(2)+rev(2)+rev(2))%mod. 对于每次操作,有多少......
  • 十四、Redis应用问题解决
    文章目录一、缓存穿透1.1问题描述1.2解决方案二、缓存击穿2.1问题描述2.2解决方案三、缓存雪崩3.1问题描述3.2解决方案四、分布式锁4.1问题描述4.2解决方案:使用redis实现分布式锁4.3编写代码4.4优化之设置锁的过期时间4.5优化之UUID防误删4.6优化之LUA脚......
  • P10676 『STA - R6』b20 题解
    题目传送门简单题,主要考察字符串。首先输入一个char类型的数组,然后输入粉丝的数量,最后直接输出数组的第一个以及粉丝的数量即可。温馨提示:提交此题时请务必将数组开大,否则你可能会获得\(90\)分高分。//『STA-R6』b20//codeby:cq_irritater//time:2024/06/30#in......
  • 【秋招突围】2024届秋招笔试-科大讯飞笔试题-03-三语言题解(Java/Cpp/Python)
    ......
  • [JLU] 数据结构与算法上机题解思路分享-第二次上机
    前言首先,请务必自己尽全力尝试实现题目,直接看成品代码,思维就被拘束了,也很容易被查重。这里只是思路解析的博客,代码仓库在JLU_Data_Structures_Record希望你能在这里找到你想要的:)正文A二叉树的创建与遍历分数10作者朱允刚单位吉林大学通过带空指针信息的先根序列(......
  • Atcoder ABC 360 全题解
    致歉对不起,我不应该在全题解的编写上咕咕咕两个月,导致流量大量流失。我知错了,下次还犯。AB无C考虑一个箱子里的所有球,我们需要把这些球放进互不相同的一些箱子里。然而我们可以留一个球在箱子里,显然留重量最大的最好,所以答案就是$\sum_{i=1}^{N}W_i$减去每个箱子里的最......
  • CF1987E 题解
    CF1987E题解题意给定一棵大小为\(n\)的有根树,各点各有一点权\(a_i\)。每次操作可以选定一节点使其点权加一,求最小的操作数,使得任一节点满足其点权不大于其所有儿子的点权之和。\(n\le5000,0\lea_i\le10^9\)题解麻了,赛后十五分钟调出来,可惜为时已晚。读懂题之后......
  • CF1375D Replace by MEX 题解
    题目大意令mexmexmex为序列中最小的没有出现的数。给你一个长度为......
  • GESP 202406 四级认证 T1 题解
    大意:一个只包含000和111的矩形,边长为......