转化思维的好题!

链接: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. C++11并发——多线程条件变量std::condition_variable(四)

    https://www.jianshu.com/p/a31d4fb5594f https://blog.csdn.net/y396397735/article/details/81272752 htt ...

  2. 在android模拟器上http 链接的图片地址可能不会显示

    AndroidStudio将targetSDK升为28后,http请求会无反应.Google表示,为保证用户数据和设备的安全,针对下一代 Android 系统(Android P) 的应用程序,将要求 ...

  3. Hibernate 基础解析(Configuration,SessionFactory,Session,Transaction,Query,Criteria)

    1       框架体系结构 对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实 现面向对象编程语言里 ...

  4. RabbitMQ入门-队列

    先建工程 下一步,输入坐标 下一步,输入工程名 先看一下最终目录 修改pom文件 <?xml version="1.0" encoding="UTF-8" ...

  5. Nginx入门篇-基础知识与linux下安装操作

    我们要深刻理解学习NG的原理与安装方法,要切合实际结合业务需求,应用场景进行灵活使用. 一.Nginx知识简述Nginx是一个高性能的HTTP服务器和反向代理服务器,也是一个 IMAP/POP3/SM ...

  6. Kolmogorov–Smirnov test(KS)

    sklearn实战-乳腺癌细胞数据挖掘( 博主亲自录制) https://study.163.com/course/introduction.htm?courseId=1005269003&u ...

  7. 使用Thumb

    目录 使用Thumb title: 使用Thumb tags: ARM date: 2018-10-24 19:28:32 --- 使用Thumb C文件使用编译选择增加 -mthumb即可,修改ma ...

  8. ELK 安装与基本配置(一)

    对于日志来说,最常见的需求就是收集.存储.查询.展示,开源社区正好有相对应的开源项目:logstash(收集).elasticsearch(存储+搜索).kibana(展示),我们将这三个组合起来的技 ...

  9. ByteBuffer详解

    注意:一定要了解这个缓冲类的几个方法和那几个字段.不然你不会明白的. 字段: position ,limit ,mark 方法:clear(), hasRemaining(),flip() 推荐博客: ...

  10. java和c#通过esb服务互调用组件

    场景:java和c#写的服务.站点,互相任意调用.实现一切即服务. 解决方案:使用这种轻量级的esb架构,通过tcp通信解决通信传输问题,总线服务解决服务地址问题,契约解决数据交互问题.由于组件封装了 ...