Educational Codeforces Round 51 (Rated for Div. 2) G. Distinctification(线段树合并 + 并查集)
题意
给出一个长度为 \(n\) 序列 , 每个位置有 \(a_i , b_i\) 两个参数 , \(b_i\) 互不相同 ,你可以进行任意次如下的两种操作 :
- 若存在 \(j \not = i\) 满足 \(a_j = a_i\) , 则可以花费 \(b_i\) 的代价令 \(a_i\) 加一 。
- 若存在 \(j\) 满足 \(a_j + 1 = a_i\) , 则可以花费 \(−b_i\) 的代价令 \(a_i\) 减一 。
定义一个序列的权值为将序列中所有 \(a_i\) 变得互不相同所需的最小代价 。 现在你要求出给定序列的每一个前缀的权值 。
\(n, a_i \le 2 \times 10^5, 1 \le b_i \le n\)
题解
以下很多拷贝自 Wearry 题解(侵删) :
考虑所有 \(a_i\) 互不相同的时候怎么做 , 若存在 \(a_i + 1 = a_j\) , 则可以花费 \(b_i − b_j\) 的代价交换两个 \(a_i\) ,
显然最优方案会将序列中所有 \(a_i\) 连续的子段操作成按 \(b_i\) 降序的 。
然后如果有 \(a_i\) 相同 , 则可以先将所有 \(a_i\) 变成互不相同的再进行排序 , 但是这时可能会扩大值域使得原本不连续的两个区间合并到一起 , 于是我们需要维护一个支持合并的数据结构 。
我们用并查集维护每个值域连续的块 , 并在每个并查集的根上维护一个以 \(b\) 为关键字的值域线段树 , 每次合并两个联通块时 , 合并他们对应的线段树即可维护答案 。
说起来好像都听懂了,但是线段树以及并查集那里实现似乎不那么明了。
考虑对于每个并查集维护对于 \(a_i\) 的一段连续的区间。如果当前的 \(a\) 已经出现过,那么我把当前的 \(a\) 扩展到当前的区间右端点 \(+1\) 。
我们发现答案是最后的 \(\sum_i a_ib_i\) 减去初始的 \(\sum_i a_ib_i\) ,因为我们对于每个 \(b_i\) 考虑,它的贡献就是它的 \(a\) 的变化值。
然后考虑线段树上维护这个信息。合并的时候,不难发现就是左区间的 \(\sum_i b_i\) 乘上右区间的元素个数,因为我们是考虑把 \(b_i\) 降序排列的。
然后就比较好 抄 实现了。。。
注意合并的时候需要定向到最右边。
代码
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
typedef long long ll;
template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;}
inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("G.in", "r", stdin);
freopen ("G.out", "w", stdout);
#endif
}
template<int Maxn>
struct Segment_Tree {
int ls[Maxn], rs[Maxn], tot[Maxn], Size;
Segment_Tree() { Size = 0; }
ll res[Maxn], val[Maxn];
inline void Push_Up(int o) {
tot[o] = tot[ls[o]] + tot[rs[o]];
val[o] = val[ls[o]] + val[rs[o]];
res[o] = res[ls[o]] + res[rs[o]] + val[ls[o]] * tot[rs[o]];
}
void Update(int &o, int l, int r, int up) {
if (!o) o = ++ Size;
if (l == r) { tot[o] = 1; val[o] = up; return ; }
int mid = (l + r) >> 1;
if (up <= mid) Update(ls[o], l, mid, up);
else Update(rs[o], mid + 1, r, up); Push_Up(o);
}
int Merge(int x, int y, int l, int r) {
if (!x || !y) return x | y;
int mid = (l + r) >> 1;
ls[x] = Merge(ls[x], ls[y], l, mid);
rs[x] = Merge(rs[x], rs[y], mid + 1, r);
Push_Up(x);
return x;
}
};
const int N = 4e5 + 1e3;
Segment_Tree<N * 20> T;
int rt[N], fa[N], lb[N], rb[N], n; ll ans = 0;
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void Merge(int x, int y) {
x = find(x); y = find(y); fa[y] = x;
ans -= T.res[rt[x]] + 1ll * lb[x] * T.val[rt[x]];
ans -= T.res[rt[y]] + 1ll * lb[y] * T.val[rt[y]];
chkmin(lb[x], lb[y]);
chkmax(rb[x], rb[y]);
rt[x] = T.Merge(rt[x], rt[y], 1, n);
ans += T.res[rt[x]] + 1ll * lb[x] * T.val[rt[x]];
}
int main () {
File();
n = read();
For (i, 1, N - 1e3)
lb[i] = rb[i] = fa[i] = i;
For (i, 1, n) {
int a = read(), b = read(), t;
ans -= 1ll * a * b;
t = rt[find(a)] ? rb[find(a)] + 1 : a;
T.Update(rt[t], 1, n, b);
ans += T.res[rt[t]] + 1ll * t * T.val[rt[t]];
if (rt[find(t - 1)]) Merge(t, t - 1);
if (rt[find(t + 1)]) Merge(t + 1, t);
printf ("%lld\n", ans);
}
return 0;
}
Educational Codeforces Round 51 (Rated for Div. 2) G. Distinctification(线段树合并 + 并查集)的更多相关文章
- Educational Codeforces Round 81 (Rated for Div. 2)E(线段树)
预处理把左集划分为大小为1~i-1时,把全部元素都移动到右集的代价,记作sum[i]. 然后枚举终态时左集的大小,更新把元素i 留在/移动到 左集的代价. 树状数组/线段树处理区间修改/区间查询 #d ...
- Educational Codeforces Round 73 (Rated for Div. 2)F(线段树,扫描线)
这道题里线段树用来区间更新(每次给更大的区间加上当前区间的权重),用log的复杂度加快了更新速度,也用了区间查询(查询当前区间向右直至最右中以当前区间端点向右一段区间的和中最大的那一段的和),也用lo ...
- Educational Codeforces Round 72 (Rated for Div. 2)E(线段树,思维)
#define HAVE_STRUCT_TIMESPEC#include<bits/stdc++.h>using namespace std;#define BUF_SIZE 100000 ...
- Educational Codeforces Round 39 (Rated for Div. 2) G
Educational Codeforces Round 39 (Rated for Div. 2) G 题意: 给一个序列\(a_i(1 <= a_i <= 10^{9}),2 < ...
- Educational Codeforces Round 51 (Rated for Div. 2) F - The Shortest Statement 倍增LCA + 最短路
F - The Shortest Statement emmm, 比赛的时候没有想到如何利用非树边. 其实感觉很简单.. 对于一个询问答案分为两部分求: 第一部分:只经过树边,用倍增就能求出来啦. 第 ...
- Educational Codeforces Round 51 (Rated for Div. 2)
做了四个题.. A. Vasya And Password 直接特判即可,,为啥泥萌都说难写,,,, 这个子串实际上是忽悠人的,因为每次改一个字符就可以 我靠我居然被hack了???? %……& ...
- Educational Codeforces Round 51 (Rated for Div. 2) The Shortest Statement
题目链接:The Shortest Statement 今天又在群里看到一个同学问$n$个$n$条边,怎么查询两点直接最短路.看来这种题还挺常见的. 为什么最终答案要从42个点的最短路(到$x,y$) ...
- 【 Educational Codeforces Round 51 (Rated for Div. 2) F】The Shortest Statement
[链接] 我是链接,点我呀:) [题意] [题解] 先处理出来任意一棵树. 然后把不是树上的边处理出来 对于每一条非树边的点(最多21*2个点) 在原图上,做dijkstra 这样就能处理出来这些非树 ...
- CodeForces Educational Codeforces Round 51 (Rated for Div. 2)
A:Vasya And Password 代码: #include<bits/stdc++.h> using namespace std; #define Fopen freopen(&q ...
随机推荐
- 2017软工实践K班总结
回首一学期的软工实践,从暑假开始陆续布置作业,经历个人.结对与团队等大小作业.也经历了不少同学被吓跑.第一周就退选的情况,能坚持下来的都是胜利者,至少你们有一颗愿意挑战的心.首先感谢助教谢涛付出的巨大 ...
- Five Dimensional Points CodeForces - 851C (计算几何+暴力)
C. Five Dimensional Points time limit per test 2 seconds memory limit per test 256 megabytes input ...
- stark组件配置,二层URL
1.django的admin配置 2 stark组件开发 3.2层url分发 4.小结 1.django的admin配置 model.py from django.db import models # ...
- 访问修饰符 public private protected default
- 封装day.js
封装day.js import dayjs from 'dayjs' import 'dayjs/locale/zh-cn' import relativeTime from 'dayjs/plugi ...
- winform启动界面+登录窗口
需求场景:先展示启动界面,然后打开登录界面,如果登录成功就跳转到主界面 首先在程序的入口路径加载启动界面,使用ShowDialog显示界面, 然后在启动界面中添加定时器,来实现显示一段时间的效果,等到 ...
- 练习MD5加密jar包编写
简介 参数签名可以保证开发的者的信息被冒用后,信息不会被泄露和受损.原因在于接入者和提供者都会对每一次的接口访问进行签名和验证. 签名sign的方式是目前比较常用的方式. 第1步:接入者把需求访问的接 ...
- Azure系列2.1.12 —— CloudBlobDirectory
(小弟自学Azure,文中有不正确之处,请路过各位大神指正.) 网上azure的资料较少,尤其是API,全是英文的,中文资料更是少之又少.这次由于公司项目需要使用Azure,所以对Azure的一些学习 ...
- Django--CRM--一级, 二级 菜单表
一. 一级菜单表 1. 首先要修改权限表的字段, 在权限表下面加上icon和 is_menu 的字段 2. 展示结果 # 我们既然想要动态生成一级菜单,那么就需要从数据库中拿出当前登录的用户的菜单表是 ...
- DAY06、元组、字典、集合
一.元组 1.定义:参数为for可以循环的对象 t1 = (1, 2) t2 = tuple((1, 2)) t3 = (1, ) #定义一个只有一个值的元组 2.常用操 ...