zoukankan      html  css  js  c++  java
  • 洛谷P5324 [BJOI2019]删数(线段树)

    洛谷P5324 [BJOI2019]删数(线段树)

    题目描述

    对于任意一个数列,如果能在有限次进行下列删数操作后将其删为空数列,则称这个数列可以删空。一次删数操作定义如下:
    >记当前数列长度为 (k) ,则删掉数列中所有等于 (k) 的数。

    现有一个长度为 (n) 的数列 (a),有 (m) 次修改操作,第 (i) 次修改后你要回答:
    经过 (i) 次修改后的数列 (a),至少还需要修改几个数才可删空?

    每次修改操作为单点修改或数列整体加一或数列整体减一。

    数据范围

    (1 le n le 150000)

    解题思路

    这题大大好评,竟然是 AGC017C 原题(这样再次增大了我和 Azusa 的差距,Azusa 做过所有的 AGC 题!)

    作为一道 AGC 题,你首先要进行转化。我们考虑什么样的数列是不需要修改的,例如:(3,3,3,4,6,6,7,10,10,10)

    发现了吗,对于一个出现过 (t) 次的权值为 c ((c le n))的数,我们把它转化为整数数轴上的一段线段 ([t-c+1,c]),如果线段正好铺满 ([1,n]),那么就是不需要修改的!

    考虑修改的最少次数是多少?有一个很好的结论:没有被覆盖的点的个数就是答案!

    有了这条性质,我们随便线段树做就行了,下面我们来证一下这个性质:

    首先没有被覆盖的点的个数是答案的下界,这点显然。

    然后我们考虑如何构造出一种方案,我们只需缩短一些线段,并使之前被覆盖的点被覆盖恰好 1 次即可。

    考虑从左扫到右,小于 1 的部分直接缩起来即可。对于当前点 x,假设 < x 的点都已经处理好了,且 x 被覆盖了大于 1 次,我们找到覆盖 x 的最小的右端点,保留,剩下的缩小一个单位即可。

    #define ls p << 1
    #define rs ls | 1
    const int S = 150000, lim = S * 3;
    const int N = S << 2;
    int mn[N<<2], mnc[N<<2], add[N<<2], cnt[N], a[N], ans, st, m, n;
    void Tag(int p, int c) { add[p] += c, mn[p] += c; }
    void spread(int p) { Tag(ls, add[p]), Tag(rs, add[p]), add[p] = 0; }
    void change(int p, int l, int r, int L, int R, int c) {
    	if (L <= l && r <= R) return Tag(p, c);
    	int mid = (l + r) >> 1; spread(p);
    	if (L <= mid) change(ls, l, mid, L, R, c);
    	if (R > mid) change(rs, mid + 1, r, L, R, c);
    	mn[p] = min(mn[ls], mn[rs]);
    	mnc[p] = (mn[ls] == mn[p] ? mnc[ls] : 0) + (mn[rs] == mn[p] ? mnc[rs] : 0);
    }
    void build(int p, int l, int r) { 
    	mnc[p] = r - l + 1; if (l == r) return; 
    	int mid = (l + r) >> 1; 
    	build(ls, l, mid), build(rs, mid + 1, r); 
    }
    void query(int p, int l, int r, int L, int R) {
    	if (L <= l && r <= R) return ans += !mn[p] ? mnc[p] : 0, void();
    	int mid = (l + r) >> 1; spread(p);
    	if (L <= mid) query(ls, l, mid, L, R);
    	if (R > mid) query(rs, mid + 1, r, L, R);
    }
    int main() {
    	read(n), read(m), st = S;
    	for (int i = 1;i <= n; ++i) read(a[i]), ++cnt[a[i] += S];
    	build(1, 1, lim);
    	for (int i = 1;i <= n; ++i)
    		if (cnt[st + i]) change(1, 1, lim, st + i - cnt[st + i] + 1, st + i, 1);
    	for (int i = 1, op, x;i <= m; ++i) {
    		read(op), read(x);
    		if (op == 0) {
    			if (x == 1) { if (cnt[st + n]) change(1, 1, lim, st + n - cnt[st + n] + 1, st + n, -1); --st; }
    			else { ++st; if (cnt[st + n]) change(1, 1, lim, st + n - cnt[st + n] + 1, st + n, 1); }
    		}
    		else {
    			int t = a[op];
    			if (t <= st + n) change(1, 1, lim, t - cnt[t] + 1, t, -1);
    			if (--cnt[t] && t <= st + n) change(1, 1, lim, t - cnt[t] + 1, t, 1);
    			t = a[op] = x + st;
    			if (cnt[t]) change(1, 1, lim, t - cnt[t] + 1, t, -1);
    			cnt[t]++, change(1, 1, lim, t - cnt[t] + 1, t, 1);
    		}
    		ans = 0, query(1, 1, lim, st + 1, st + n), write(ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Cocos2d Box2D之动态刚体
    Cocos2d Box2D之简介
    Cocos2d-x之物理引擎
    Cocos2d-x之UI控件简介
    Cocos2d-x之数据的处理
    My First Django Project (3)
    My First Django Project (2)
    My First Django Project
    Python 学习笔记
    利用python scrapy 框架抓取豆瓣小组数据
  • 原文地址:https://www.cnblogs.com/Hs-black/p/14321868.html
Copyright © 2011-2022 走看看