「LibreOJ Round #6」花火
转化思维的好题!
链接:here
大致题意:
有$ n$个数字,你每次可以交换相邻两个,还有一次交换任意两个元素的机会,求最少的交换次数使得这些数字升序排序(原数列两两不同)
$ solotion:$
首先有一个结论:交换任意两个元素可以选择在第一次交换,且一定不会劣
证明:假设不在第一次交换,可以通过倒推这次交换的贡献,使得这次机会平移到第一次交换,结果不变
第二个结论:交换相邻两个元素的次数等于逆序对数
证明:略
第三个结论:交换两个元素$ x,y$,所能够减少的逆序对数量等价于把每个数$ a_i$对应到坐标$ (i,a_i)$之后矩形{$(x,a_x),(y,a_y)$}中的点数$ *2+1$(不包含边界)
证明:在矩形内的每个点,原先会贡献$ 2$的逆序对,交换后将不再产生这样的贡献。$ +1$是因为交换本身会减少一个逆序对
也就是说我们实际要求的等价于找到一个矩形(两个角都在点上),使得矩形内点尽量多
这并不容易直接处理,考虑转化题意
选择的矩形两个角$(x,a_x),(y,a_y)$有性质如下:$(x<y)$
$ 1.a_x>a_y$ 证明:否则会增加逆序对数量,肯定不优
$ 2.a_x$是前缀最大值,$ a_y$是后缀最小值
证明:如果$ a_x$不是前缀最大值,一定有一个点$ (k,a_k)$在$ (x,a_x)$的左上方,这样的矩形能够完全包含矩形{$(x,a_x),(y,a_y)$}使得$ (x,a_x)$不可能成为最优,后缀最小值同理。
这样我们获得了一个前缀最大值数组$ S$,一个后缀最小值数组$ T$,我们对于每个点,计算哪些矩形能够包含这个点
显然包含这个点的矩形左端点要在这个点的左上方,右端点在这个点的右下方,可以通过两次二分得到包含这个点的两段数组区间
我们用$ (x,y)$表示左端点是前缀最大值第$ x$个,右端点是后缀最小值第$ y$个的矩形,可以发现能包含这个点的矩形用坐标表示后也是一个矩形
然后就把题意转化成有若干个矩形,求一个被最多矩形覆盖的位置 扫描线+线段树维护即可
时间复杂度:$ O(n log n)$
code:
#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define file(x)freopen(x".in","r",stdin);freopen(x".out","w",stdout)
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
ll x = ; char zf = ; char ch = getchar();
while (ch != '-' && !isdigit(ch)) ch = getchar();
if (ch == '-') zf = -, ch = getchar();
while (isdigit(ch)) x = x * + ch - '', ch = getchar(); return x * zf;
}
void write(ll y){if(y<)putchar('-'),y=-y;if(y>)write(y/);putchar(y%+);}
void writeln(const ll y){write(y);putchar('\n');}
int i,j,k,m,n,x,y,z,cnt;
int a[],c[];
void up(int x){
for(rt i=x;i<=n;i+=i&-i)c[i]++;
}
int query(int x){
int ans=;
for(rt i=x;i;i&=i-)ans+=c[i];
return ans;
}
int sta[],top;
int L1[],R1[],L2[],R2[];
struct query{
int id,x,L,R;
bool operator <(const query s)const{
if(x==s.x)return id<s.id;
return x<s.x;
}
}q[];
struct segment{
int L,R,Max,tag;
}t[*];
void build(int x,int L,int R){
t[x].L=L;t[x].R=R;if(L==R)return;
const int mid=L+R>>;
build(x<<,L,mid);build(x<<|,mid+,R);
}
void change(int x,int L,int R,int val){
if(t[x].L>=L&&t[x].R<=R){
t[x].tag+=val;
t[x].Max+=val;
return;
}
const int mid=t[x].L+t[x].R>>;
if(mid>=L)change(x<<,L,R,val);
if(mid+<=R)change(x<<|,L,R,val);
t[x].Max=max(t[x<<].Max,t[x<<|].Max)+t[x].tag;
}
int main(){
n=read();ll ret=(ll)n*(n-)/;
for(rt i=;i<=n;i++)a[i]=read();
for(rt i=;i<=n;i++){
ret-=query(a[i]-);
up(a[i]);
}//ret计算初始逆序对数量
for(rt i=;i<=n;i++){
if(a[i]>sta[top]||!top)sta[++top]=a[i];
int pla=lower_bound(sta+,sta+top+,a[i])-sta;
L1[i]=pla;L2[i]=top;
}top=;
memset(sta,,sizeof(sta));
for(rt i=n;i>=;i--){
if(-a[i]>sta[top]||!top)sta[++top]=-a[i];
int pla=lower_bound(sta+,sta+top+,-a[i])-sta;
R1[i]=pla;R2[i]=top;
}//L1 L2 R1 R2表示包含第i个点的合法矩形的范围
for(rt i=;i<=n;i++){
q[*i-]={,L1[i],R1[i],R2[i]};
q[*i]={-,L2[i]+,R1[i],R2[i]};
}
sort(q+,q+*n+);int ttt=;
build(,,top);
for(rt i=;i<=*n;i++){
change(,q[i].L,q[i].R,q[i].id);
ttt=max(ttt,t[].Max);
}
cout<<ret-(ttt-)*;
return ;
}
「LibreOJ Round #6」花火的更多相关文章
- loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分
$ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinob ...
- [LOJ535]「LibreOJ Round #6」花火
loj description 给你一个排列\(h_i\),你需要交换任意两个位置上的数使得交换后排列的逆序对数最少. \(n \le 3\times 10^5\) sol 首先可以发现,如果交换两个 ...
- loj #547. 「LibreOJ β Round #7」匹配字符串
#547. 「LibreOJ β Round #7」匹配字符串 题目描述 对于一个 01 串(即由字符 0 和 1 组成的字符串)sss,我们称 sss 合法,当且仅当串 sss 的任意一个长度为 ...
- [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}的一个子集,使得这个子集中任意两 ...
随机推荐
- [AC自动机][学习笔记]
用途 AC自动机适用于一类用多个子串在模板串中匹配的字符串问题. 也就是说先给出一个模板串,然后给出一些子串.要求有多少个子串在这个模板串中出现过. KMP与trie树 其实AC自动机就是KMP与tr ...
- 关于苹果手机微信端 position: fixed定位top导航栏问题
在移动端,position: fixed定位一般不被识别,或者在ios系统中,获取input焦点时,会导致position: fixed的top失效,下面是我验证过的方法,大家可以试试.<!do ...
- prototype 与 proto的关系是什么:
__proto__是什么? 我们在这里简单地说下.每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性 时,如果这个对象内部不存在这个属性,那么他就会去__proto_ ...
- 关于python类变量和实例变量
今天在看python的类和实例的时候,突然发现了一个以前遗漏的点,也就是类变量和实例变量.首先需要理解一下类变量和实例变量的概念. 类全局变量:在类中定义,对类和由类生成的实例生效,如果通过方法对类变 ...
- (基础)codeVs2235 机票打折
题目描述 Description .输入机票原价(3到4位的正整数,单位:元),再输入机票打折率(小数点后最多一位数字).编程计算打折后机票的实际价格(单位:元.计算结果要将个位数四舍五入到十位数“元 ...
- Python模块初识
目录 一 模块初识 二 模块分类 三 导入模块 四 Python文件的两种用途 五 模板查找顺序 六 软件开发目录规范 一.模块初识 模块是自我包含并且有组织的代码片段,是一系列功能的集合体,一个py ...
- XML异常
1.com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效 ...
- SpringBoot笔记十五:任务
目录 异步任务 定时任务 异步任务 注解:@Async,@EnableAsync 我新建一个Service,就叫AsyncService package com.example.service; im ...
- js替换全部,js检查输入的日期是否是一个正确的日期格式
<script language="javascript"> var str = "我爱的人和爱我的人,我爱的人和爱我的人"; var newstr ...
- HTML第四耍 超链接标签
1.HTML 超链接(链接) 一.HTML 超链接 HTML中使用超级链接与网络上的另一个文档相连.几乎可以在所有的网页中找到链接.点击链接可以从一张页面跳转到另一张页面. 超链接可以是一个字,一个词 ...