loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分
$ \color{#0066ff}{ 题目描述 }$
「Hanabi, hanabi……」
一听说祭典上没有烟火,Karen 一脸沮丧。
「有的哦…… 虽然比不上大型烟花就是了。」
还好 Shinobu 早有准备,Alice、Ayaya、Karen、Shinobu、Yoko 五人又能继续愉快地玩耍啦!
「噢……!不是有放上天的烟花嘛!」Karen 兴奋地喊道。
「啊等等……」Yoko 惊呼。Karen 手持点燃引信的烟花,「嗯??」
Yoko 最希望见到的是排列优美的烟火,当然不会放过这个机会…… 不过时间似乎已经不多了。
nn 个烟火排成一排,从左到右高度分别为 \(h_1,h_2,\cdots\),这些高度两两不同。
每次 Yoko 可以选择两个相邻的烟火交换,这样的交换可以进行任意多次。
每次 Yoko 还可以选择两个不相邻的烟火交换,但这样的交换至多进行一次。
你的任务是帮助 Yoko 用最少次数的交换,使这些烟火从左到右的高度递增。
\(\color{#0066ff}{输入格式}\)
第一行包含一个正整数 \(n\)。
第二行包含 \(n\) 个正整数 \(h_1,h_2,\cdots\),相邻整数之间用一个空格隔开。
\(\color{#0066ff}{输出格式}\)
输出一个整数,表示最少的交换次数。
\(\color{#0066ff}{输入样例}\)
5
3 5 4 1 2
\(\color{#0066ff}{输出样例}\)
5
\(\color{#0066ff}{数据范围与提示}\)
一开始,\(5\) 个烟火的高度依次为 \(3,5,4,1,2\)。
第 \(1\) 次,交换第 \(4\) 根烟火和第 \(5\) 根烟火,交换后烟火的高度依次为 \(3,5,4,2,1\)。
第 \(2\) 次,交换第 \(3\) 根烟火和第 \(4\) 根烟火,交换后烟火的高度依次为 \(3,5,2,4,1\)。
第 \(3\) 次,交换第 \(1\) 根烟火和第 \(2\) 根烟火,交换后烟火的高度依次为 \(5,3,2,4,1\)。
第 \(4\) 次,交换第 \(2\) 根烟火和第 \(3\) 根烟火,交换后烟火的高度依次为 \(5,2,3,4,1\)。
第 \(5\) 次,交换第 \(1\) 根烟火和第 \(5\) 根烟火,交换后烟火的高度依次为 \(1,2,3,4,5\)。
可以证明这是交换次数最少的方案。

\(\color{#0066ff}{题解}\)
考虑没有交换任意两个数一次的操作,那么答案就是逆序对数
现在我们有一个交换任意两个数的操作
我们肯定是要让这次操作的贡献最大的
也就是说,减少的逆序对数最多
那么我们选的两个数,左面那个肯定是越靠左且越大为优,右面那个越靠右越小越优
也就是说,我们可能被交换的数就是所有前缀max取得的点和所有后缀min取得的点
然后我们处理出了这两个可能修改的位置数组,都是单调的
我们现在要在两个数组内分别选一个数,交换这两个数所对应的位置,使得对答案的贡献最大
对于一对\([l,r]\),交换它俩的贡献是\([l,r]之间权值在[a[r],a[l]]之间的值的个数\)
这就是二维数点了, 主席树可以维护
现在考虑怎么快速求出每个点的贡献
也就是说,对于每个可能的右端点,选一个最优的左端点

我们假设1选择y最优
那么可以得出\(G+H>F+I\)
然后对于2,因为\(G+H+J>F\)显然,所以y肯定比x有,也就是说最优点单调!!
于是我们就可以整体二分\(O(nlog^2n)\)的求出右面每个点的贡献,然后取max,交换最优的l和r
最后树状数组求一下序列逆序对即可
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 3e5 + 10;
struct Tree {
protected:
int st[maxn];
int low(int x) { return x & (-x); }
int siz;
public:
void resize(int n) { siz = n; }
void add(int pos) { while(pos <= siz) st[pos]++, pos += low(pos); }
int query(int pos) { int re = 0; while(pos) re += st[pos], pos -= low(pos); return re; }
}s;
struct node {
node *ch[2];
int num;
node(int num = 0): num(num) { ch[0] = ch[1] = NULL; }
};
int n, a[maxn];
node *root[maxn];
int st1[maxn], st2[maxn], top1, top2, to[maxn], ans[maxn];
LL tot;
void init() {
root[0] = new node();
root[0]->ch[0] = root[0]->ch[1] = root[0];
}
void add(node *&o, node *lst, int l, int r, int p) {
o = new node(), *o = *lst, o->num++;
if(l == r) return;
int mid = (l + r) >> 1;
if(p <= mid) add(o->ch[0], lst->ch[0], l, mid, p);
else add(o->ch[1], lst->ch[1], mid + 1, r, p);
}
int getnum(node *x, node *y, int l, int r, int ql, int qr) {
if(x->num == y->num) return 0;
if(ql <= l && r <= qr) return y->num - x->num;
int mid = (l + r) >> 1;
int cnt = 0;
if(ql <= mid) cnt += getnum(x->ch[0], y->ch[0], l, mid, ql, qr);
if(qr > mid) cnt += getnum(x->ch[1], y->ch[1], mid + 1, r, ql, qr);
return cnt;
}
int getans(int l, int r) {
if(l >= r) return 0;
if(a[l] < a[r]) return 0;
return getnum(root[l - 1], root[r], 1, n, a[r] + 1, a[l] - 1);
}
void cdq(int l, int r, int a, int b) {
if(a > b) return;
if(l > r) return;
if(l == r) {
for(int i = a; i <= b; i++) {
int now = getans(st1[l], st2[i]);
if(now > ans[i]) ans[i] = now, to[i] = l;
}
return;
}
int mid = (a + b) >> 1;
//printf("now is work[%d, %d, %d, %d]\n", l, r, a, b);
ans[mid] = 0;
for(int i = l; i <= r; i++) {
int now = getans(st1[i], st2[mid]);
if(now >= ans[mid]) ans[mid] = now, to[mid] = i;
}
cdq(l, to[mid], a, mid - 1);
cdq(to[mid], r, mid + 1, b);
}
void predoit() {
s.resize(n);
init();
for(int i = 1; i <= n; i++) add(root[i], root[i - 1], 1, n, a[i] = in());
//st1 can be left
//st2 can be right
for(int i = 1; i <= n; i++) {
while(top2 && a[i] < a[st2[top2]]) top2--;
if(a[i] > a[st1[top1]]) st1[++top1] = i;
st2[++top2] = i;
}
int max = 0, l = 0, r = 0;
cdq(1, top1, 1, top2);
for(int i = 1; i <= top2; i++)
if(max < ans[i]) max = ans[i], l = st1[to[i]], r = st2[i];
std::swap(a[l], a[r]);
if(l ^ r) tot++;
}
void getans() {
for(int i = n; i >= 1; i--) {
tot += s.query(a[i]);
s.add(a[i]);
}
printf("%lld\n", tot);
}
int main() {
n = in();
predoit();
getans();
return 0;
}
loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分的更多相关文章
- [LOJ#531]「LibreOJ β Round #5」游戏
[LOJ#531]「LibreOJ β Round #5」游戏 试题描述 LCR 三分钟就解决了问题,她自信地输入了结果-- > -- 正在检查程序 -- > -- 检查通过,正在评估智商 ...
- [LOJ#530]「LibreOJ β Round #5」最小倍数
[LOJ#530]「LibreOJ β Round #5」最小倍数 试题描述 第二天,LCR 终于启动了备份存储器,准备上传数据时,却没有找到熟悉的文件资源,取而代之的是而屏幕上显示的一段话: 您的文 ...
- [LOJ#516]「LibreOJ β Round #2」DP 一般看规律
[LOJ#516]「LibreOJ β Round #2」DP 一般看规律 试题描述 给定一个长度为 \(n\) 的序列 \(a\),一共有 \(m\) 个操作. 每次操作的内容为:给定 \(x,y\ ...
- [LOJ#515]「LibreOJ β Round #2」贪心只能过样例
[LOJ#515]「LibreOJ β Round #2」贪心只能过样例 试题描述 一共有 \(n\) 个数,第 \(i\) 个数 \(x_i\) 可以取 \([a_i , b_i]\) 中任意值. ...
- [LOJ#525]「LibreOJ β Round #4」多项式
[LOJ#525]「LibreOJ β Round #4」多项式 试题描述 给定一个正整数 k,你需要寻找一个系数均为 0 到 k−1 之间的非零多项式 f(x),满足对于任意整数 x 均有 f(x) ...
- [LOJ#526]「LibreOJ β Round #4」子集
[LOJ#526]「LibreOJ β Round #4」子集 试题描述 qmqmqm有一个长为 n 的数列 a1,a2,……,an,你需要选择集合{1,2,……,n}的一个子集,使得这个子集中任意两 ...
- [LOJ#522]「LibreOJ β Round #3」绯色 IOI(危机)
[LOJ#522]「LibreOJ β Round #3」绯色 IOI(危机) 试题描述 IOI 的比赛开始了.Jsp 和 Rlc 坐在一个角落,这时他们听到了一个异样的声音 …… 接着他们发现自己收 ...
- loj #547. 「LibreOJ β Round #7」匹配字符串
#547. 「LibreOJ β Round #7」匹配字符串 题目描述 对于一个 01 串(即由字符 0 和 1 组成的字符串)sss,我们称 sss 合法,当且仅当串 sss 的任意一个长度为 ...
- [LOJ535]「LibreOJ Round #6」花火
loj description 给你一个排列\(h_i\),你需要交换任意两个位置上的数使得交换后排列的逆序对数最少. \(n \le 3\times 10^5\) sol 首先可以发现,如果交换两个 ...
随机推荐
- 页面中CSS的四种引入方式的介绍与比较
转自:https://blog.csdn.net/qq_38689666/article/details/79039392 一:行内式 <p style="color:red" ...
- 【284】◀▶ arcpy.da & arcpy 数据访问模块
使用游标访问数据 数据访问模块 (arcpy.da) 参考: ArcGIS Python编程案例(9)-ArcPy数据访问模块 读取几何 写入几何 使用 Python 指定查询 01 da.Sea ...
- Idea的基本介绍
Idea的基本介绍 Idea一般是作为一个Java和Scala的开发工具来使用的,它的使用方法和Eclipse有一些不同,这里记录以下一些基本点. 1.创建项目 创建一个新项目的时候,用户必须选择一个 ...
- Oracle字符集的查看查询和Oracle字符集的设置修改(转)
最近郁闷的字符集2014年7月31日16:32:58 本文主要讨论以下几个部分:如何查看查询oracle字符集. 修改设置字符集以及常见的oracle utf8字符集和oracle exp 字符集问题 ...
- webmagic使用
webmagic是Java语言用于爬虫的工具.官网地址:http://webmagic.io/,中文文档地址:http://webmagic.io/docs/zh/ 使用webmagic有3种配置需要 ...
- Loadrunner Analyze
Analysis 对controller运行的结果进行分析 1.首先必须明确:光靠Analysis是不行的,只要能通过Analysis分析出部分问题就已经很不错了, 善于利用它才是最关键的. 2.如何 ...
- C#中的异步编程Async 和 Await
谈到C#中的异步编程,离不开Async和Await关键字 谈到异步编程,首先我们就要明白到底什么是异步编程. 平时我们的编程一般都是同步编程,所谓同步编程的意思,和我们平时说的同时做几件事情完全不同. ...
- 1.python系统性能信息模块之psutil模块
Psutil(进程和系统实用程序)是一个跨平台的库,用于在Python中检索有关运行进程和系统资源利用率(CPU,内存,磁盘,网络)的信息.它主要用于系统监视,分析和限制系统资源及运行进程的管理.它实 ...
- easyUI Methods
doc对象转jQuery 对象 $(doc Object); jQuery Object.控件名('方法'[,参数]); options 为该控件的属性 方式一: var opts = $('.eas ...
- Oracle ERP系统借贷关系表
Oracle ERP系统借贷关系表 成本核算会计信息归纳 按照事务处理的来源类型归纳. 一. 采购接收入库和退货: 1.接收: 借:材料采购 (订单价格) 贷:应计负债 (订单价格) 2.入库: ...