转化思维的好题!

链接: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」花火的更多相关文章

  1. loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分

    $ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinob ...

  2. [LOJ535]「LibreOJ Round #6」花火

    loj description 给你一个排列\(h_i\),你需要交换任意两个位置上的数使得交换后排列的逆序对数最少. \(n \le 3\times 10^5\) sol 首先可以发现,如果交换两个 ...

  3. loj #547. 「LibreOJ β Round #7」匹配字符串

    #547. 「LibreOJ β Round #7」匹配字符串   题目描述 对于一个 01 串(即由字符 0 和 1 组成的字符串)sss,我们称 sss 合法,当且仅当串 sss 的任意一个长度为 ...

  4. [LOJ#531]「LibreOJ β Round #5」游戏

    [LOJ#531]「LibreOJ β Round #5」游戏 试题描述 LCR 三分钟就解决了问题,她自信地输入了结果-- > -- 正在检查程序 -- > -- 检查通过,正在评估智商 ...

  5. [LOJ#530]「LibreOJ β Round #5」最小倍数

    [LOJ#530]「LibreOJ β Round #5」最小倍数 试题描述 第二天,LCR 终于启动了备份存储器,准备上传数据时,却没有找到熟悉的文件资源,取而代之的是而屏幕上显示的一段话: 您的文 ...

  6. [LOJ#516]「LibreOJ β Round #2」DP 一般看规律

    [LOJ#516]「LibreOJ β Round #2」DP 一般看规律 试题描述 给定一个长度为 \(n\) 的序列 \(a\),一共有 \(m\) 个操作. 每次操作的内容为:给定 \(x,y\ ...

  7. [LOJ#515]「LibreOJ β Round #2」贪心只能过样例

    [LOJ#515]「LibreOJ β Round #2」贪心只能过样例 试题描述 一共有 \(n\) 个数,第 \(i\) 个数 \(x_i\) 可以取 \([a_i , b_i]\) 中任意值. ...

  8. [LOJ#525]「LibreOJ β Round #4」多项式

    [LOJ#525]「LibreOJ β Round #4」多项式 试题描述 给定一个正整数 k,你需要寻找一个系数均为 0 到 k−1 之间的非零多项式 f(x),满足对于任意整数 x 均有 f(x) ...

  9. [LOJ#526]「LibreOJ β Round #4」子集

    [LOJ#526]「LibreOJ β Round #4」子集 试题描述 qmqmqm有一个长为 n 的数列 a1,a2,……,an,你需要选择集合{1,2,……,n}的一个子集,使得这个子集中任意两 ...

随机推荐

  1. 灰度发布/AB test

    背景 互联网产品有一个特点,就是不停的升级,升级,再升级.一般采用敏捷开发的团队,基本上保持每周一次的发布频率,系统升级总是伴随着风险,新旧版本兼容的风险,用户使用习惯突然改变而造成用户流失的风险,系 ...

  2. python基础-守护进程、守护线程、守护非守护并行

    守护进程 1.守护子进程 主进程创建守护进程  其一:守护进程会在主进程代码执行结束后就终止  其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic pro ...

  3. 一个很适合初学者的selenium教程

    http://www.cnblogs.com/hustar0102/p/5885115.html

  4. HTML学习笔记Day2

    一.部分表单元素的使用 1.表单的作用:用来收集用户信息 2.表单元素 (1)表单控件: 单行文本框:<input  type="text" value="默认值& ...

  5. windows下配置host不生效问题

    今天遇到了host配置之后不生效的问题,原因是文本编辑器用的是非windows格式,再notepad++右下角切换一下即可,将notepad++ 视图-显示符号-显示所有字符勾上之后可以看到windo ...

  6. Oracle基础--创建临时表空间/表空间/创建用户/授权

    总结:创建用户一般分四步: 第一步:创建临时表空间(创建用户之前要创建"临时表空间",若不创建则默认的临时表空间为temp.) SQL> CREATE TEMPORARY T ...

  7. 运维监控-Zabbix Server 使用QQ SMTP发送邮件报警及定制报警内容

    运维监控-Zabbix Server 使用QQ SMTP发送邮件报警及定制报警内容 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客采用腾讯邮箱,想必大家都对QQ很了解,所以 ...

  8. 《Apache kafka实战》读书笔记-管理Kafka集群安全之ACL篇

    <Apache kafka实战>读书笔记-管理Kafka集群安全之ACL篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 想必大家能看到这篇博客的小伙伴,估计你对kaf ...

  9. Netsarang

    下载 https://www.netsarang.com/zh/all-downloads/ 建议直接下载 xmanager-power-suite,里面包含了 Xmanager.Xshell.Xft ...

  10. JAVA核心技术I---JAVA基础知识(多态)

    一:类转型 类转型:子类可以转父类,父类不可以转子类(除非父类对象本身就是子类) 1.父类 public class Human { public void say() { System.out.pr ...