• 【CF1051G】 Distinctification 题解


    很感谢这道花费了我一整个上午的题目,很感谢。

    CF 传送门

    线段树合并 + 并查集维护。

    Description

    Solution

    以下使用 a i a_i ai 代替 A i A_i Ai b i b_i bi 同理。

    1 观察

    观察这两个操作,容易发现如果新加进来一个数,想要通过操作这个数使得序列中没有重复的数,只能是把这个新数不断加一直到没有数与它相同。通过这个观察,可以得到结论:最终 a i a_i ai 构成的集合是确定的。注意,是集合确定。

    进一步观察这两个操作,就能发现:对于 1 ≤ i ,   j ≤ n 1\leq i,\ j \leq n 1i, jn,若 a i = a j + 1 a_i = a_j + 1 ai=aj+1,可以通过花费 b j − b i b_j-b_i bjbi 的代价,将两者交换。交换有什么好处?若 b i < b j b_i < b_j bi<bj,则 a i × b i + a j × b j ≥ a i × b j + a j × b i a_i \times b_i + a_j \times b_j \geq a_i \times b_j + a_j \times b_i ai×bi+aj×bjai×bj+aj×bi

    同时,不妨假设最后满足条件的整个操作后序列每项为 c i ,   1 ≤ i ≤ n c_i,\ 1\leq i \leq n ci, 1in。那么总共所需代价即为 ∑ i = 1 n ( c i − a i ) × b i \sum_{i=1}^n (c_i-a_i) \times b_i i=1n(ciai)×bi。为什么?因为若 a i a_i ai 加了 1 1 1,代表花费增加了 b i b_i bi;若 a i a_i ai 减去 1 1 1,代表花费减少了 b i b_i bi。那么最后 a i a_i ai 总共增加了 ( c i − a i ) (c_i-a_i) (ciai),但是内部过程是如何的我们实际上并不会关注,因为除了 ( c i − a i ) × b i (c_i-a_i) \times b_i (ciai)×bi 之外 a i a_i ai 带来的花费已经抵消掉了。故最终的花费总共为 ∑ i = 1 n ( c i − a i ) × b i \sum_{i=1}^n (c_i-a_i) \times b_i i=1n(ciai)×bi。进一步拆分,得总代价为 ∑ i = 1 n c i × b i − ∑ i = 1 n a i × b i \sum_{i=1}^n c_i \times b_i - \sum_{i=1}^na_i \times b_i i=1nci×bii=1nai×bi,而式子中的第二项已经确定,所以我们想要 c i × b i c_i\times b_i ci×bi 的总和尽可能小。

    综上两段分析,我们明白:我们需要做的是通过交换 a a a 中元素的位置,即重新排序使得答案最小。

    因为交换 a i a_i ai a j a_j aj 的前提条件是 a i = a j + 1 a_i=a_j+1 ai=aj+1,故我们只能在⼀个 a i a_i ai 的连续段内任意调整顺序。为了不破坏连续段的单调递增性,我们将调换 a i a_i ai 的顺序转化为调换 b i b_i bi 的顺序,这并不影响结果。再结合交换两项带来的优势,不难得出:我们要在一个 a a a 的连续段内将其相对应的 b b b 中的项从大到小排序,然后与 a a a 中的项一一对应,从而达到 ∑ i = 1 n c i × b i \sum_{i=1}^n c_i\times b_i i=1nci×bi 尽可能小的目的。

    2 求每个连续段花费

    为了维护连续段并且支持合并(加入新的数),我们考虑线段树合并。 r t i rt_i rti 表示段内最小的数为 i i i 的连续段(此时已保证 a i a_i ai 两两互不相同)相对应的一棵权值线段树。线段树内部以 b i b_i bi 为下标。

    显然,在 r t i rt_i rti 这棵权值线段树内,每个 b i b_i bi 从小到大排好了序。而我们要求的,就是这些 b i b_i bi 从大到小 分别乘上 对应最小值为 i i i 的从小到大的连续段的每一项。我们可以明确一点:每个 b i b_i bi 都会至少乘上 i i i,故可以直接将 t r t i . s u m × i t_{rt_i}.sum \times i trti.sum×i 统计进 a n s ans ans,之后加入每个 b i b_i bi b i b_i bi 从小到大升序排列)乘上 a a a 中第 t r t i . s i z e − i + 1 t_{rt_i}.size - i + 1 trti.sizei+1 项的两者之积。

    那怎么快速求得这些乘积的和呢?其实只需要在递归线段树的时候,每一层都给 a n s ans ans 加上 t l s . s u m × t r s . s i z t_{ls}.sum \times t_{rs}.siz tls.sum×trs.siz 即可。可以感性理解为每一层都将 [ l , m i d ] [l,mid] [l,mid] [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 这两个区间调换了位置,那最后整个 b b b 序列肯定被排为从大到小的降序排列。而上面的 t l s . s u m × t r s . s i z t_{ls}.sum \times t_{rs}.siz tls.sum×trs.siz 就是将区间 [ l , m i d ] [l,mid] [l,mid] 移位到了区间 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 之后所多出的花费。

    3 统计加数后答案

    知道如何统计每个连续段的贡献之后,分析一下每次新加入一个数之后如何重新统计答案。流程如下:

    1. 找到输入的这个数 a i a_i ai 所在的连续段中,最大的数是多少。而 a i a_i ai,就要赋值为这个最大值加一的数,记为 y y y
    2. 统计将 a i a_i ai 变为 y y y 所需的代价,加进 a n s ans ans。此代价即为 ( y − a i ) × b i (y-a_i)\times b_i (yai)×bi
    3. y y y 暂时独立成为一个连续段,将 b i b_i bi 记入 t r t y t_{rt_{y}} trty 中。
    4. r t y rt_y rty 与左右相邻的段的权值线段树合并(前提是要合并段不为空),左右相邻的连续段即为 r t y − 1 rt_{y-1} rty1 r t y + 1 rt_{y+1} rty+1
    5. 合并时已经重新统计过新的连续段的代价,故此时直接输出答案即可。

    并查集维护什么?当然就是第一步中每个数所在的连续段的最小值(即左端点),同时再开一个数组维护相应右端点即可。

    为了保证不重复,每个 a i a_i ai 都要加 1 1 1 加到不与之前数重复,所以极端情况下 a i a_i ai 可能加到 2 × 2 e 5 2 \times 2e5 2×2e5,故数组要开两倍。

    至此,该题已彻底解决,时复 O ( n log ⁡ n ) O(n \log n) O(nlogn)

    Code

    #include
    using namespace std;
    
    #define ll long long 
    #define rep(i, a, b) for(register int i = a; i <= b; ++i)
    const int maxn = 4e5 + 10, maxm = maxn * 30;
    int n, rt[maxn], fa[maxn], bl[maxn];
    ll ans;
    
    inline int fnd(int x){
    	return x == fa[x] ? x : fa[x] = fnd(fa[x]);
    }
    
    struct segment_tree{
    	#define L(x) ch[0][x]
    	#define R(x) ch[1][x]
    	#define ls L(x)
    	#define rs R(x)
    	#define sz(x) t[x].siz
    	#define sm(x) t[x].sum
    	struct tree{
    		int siz; ll sum;
    	}t[maxm]; int tot, ch[2][maxm];
    	inline void up(int x){
    		sm(x) = sm(ls) + sm(rs), sz(x) = sz(ls) + sz(rs);
    	}
    	inline void updt(int &x, int l, int r, int p){
    		if(!x) x = ++tot;
    		if(l == r){
    			sz(x) = 1, sm(x) = p; return;
    		} int mid = l + r >> 1;
    		p <= mid ? updt(ls, l, mid, p) : updt(rs, mid + 1, r, p);
    		up(x);
    	}
    	inline int merge(int a, int b, int l, int r){
    		if(!a or !b) return a + b;
    		int x = ++tot; if(l == r) return x;
    		ans -= sm(L(a)) * sz(R(a)) + sm(L(b)) * sz(R(b));
    		int mid = l + r >> 1;
    		ls = merge(L(a), L(b), l, mid), rs = merge(R(a), R(b), mid + 1, r);
    		ans += sm(ls) * sz(rs), up(x); return x;
    	}
    	inline void link(int a, int b){
    		a = fnd(a), b = fnd(b), fa[b] = a;
    		ans -= sm(rt[a]) * a + sm(rt[b]) * b;
    		rt[a] = merge(rt[a], rt[b], 1, n);
    		ans += sm(rt[a]) * a, bl[a] = bl[b];
    	}
    }T;
    
    int main(){ rep(i, 1, maxn - 5) fa[i] = bl[i] = i;
    	scanf("%d", &n); 
    	rep(i, 1, n){ int x, y, v;
    		scanf("%d%d", &x, &v);
    		y = rt[x] ? bl[fnd(x)] + 1 : x;
    		ans += 1ll * (y - x) * v;
    		T.updt(rt[y], 1, n, v);
    		if(rt[y - 1]) T.link(y - 1, y); if(rt[y + 1]) T.link(y, y + 1);
    		printf("%lld\n", ans);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    感谢阅读。

  • 相关阅读:
    【ESP8266点焊机】基于 ESP8266 for Arduino
    MyBatis-----10、MyBatis逆向工程
    三维GIS的业务导向
    Web Component -- 即将爆发的原生的 UI 组件化标准
    C Primer Plus(6) 中文版 第9章 函数 9.7 指针简介 9.8 关键概念
    Leetcode《图解数据结构》刷题日志【第三周】(2022/10/31-2022/11/06)
    【信息融合】基于BP神经网络和DS 证据理论实现不确定性信息融合问题附matlab代码
    计算机网络4小时速成:传输层,功能,UDP协议,TCP协议,三次握手,传输数据,四次握手,超时重传,流量控制
    如何创建加载项(1)
    Verilog参数、Verilog参数和属性冲突、整数处理
  • 原文地址:https://blog.csdn.net/ez_gsn/article/details/126123634