【CF1051G】 Distinctification 题解
很感谢这道花费了我一整个上午的题目,很感谢。
CF 传送门
线段树合并 + 并查集维护。
Update
- 2023.03.15:题意描述的图炸了,修图。
Description
题意概述。
Solution
以下使用
1 观察
观察这两个操作,容易发现如果新加进来一个数,想要通过操作这个数使得序列中没有重复的数,只能是把这个新数不断加一直到没有数与它相同。通过这个观察,可以得到结论:最终
进一步观察这两个操作,就能发现:对于
同时,不妨假设最后满足条件的整个操作后序列每项为
综上两段分析,我们明白:我们需要做的是通过交换
因为交换
2 求每个连续段花费
为了维护连续段并且支持合并(加入新的数),我们考虑线段树合并。
显然,在
那怎么快速求得这些乘积的和呢?其实只需要在递归线段树的时候,每一层都给
3 统计加数后答案
知道如何统计每个连续段的贡献之后,分析一下每次新加入一个数之后如何重新统计答案。流程如下:
- 找到输入的这个数
a_i 所在的连续段中,最大的数是多少。而a_i ,就要赋值为这个最大值加一的数,记为y 。 - 统计将
a_i 变为y 所需的代价,加进ans 。此代价即为(y-a_i)\times b_i 。 -
- 将
rt_y 与左右相邻的段的权值线段树合并(前提是要合并段不为空),左右相邻的连续段即为rt_{y-1} 和rt_{y+1} 。 - 合并时已经重新统计过新的连续段的代价,故此时直接输出答案即可。
并查集维护什么?当然就是第一步中每个数所在的连续段的最小值(即左端点),同时再开一个数组维护相应右端点即可。
为了保证不重复,每个
至此,该题已彻底解决,时复
Code
#include<bits/stdc++.h>
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;
}
感谢阅读。