转化思维的好题!

链接: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. Vue+koa2开发一款全栈小程序(4.Koa入门)

    1.Koa是什么? 基于nodejs平台的下一代web开发框架 1.Express原班人马打造,更精简 2.Async+await处理异步 3.洋葱圈型的中间件机制 新建一个koa项目 1.打开cmd ...

  2. 解决MySQL5.7密码重置问题

    前言:最近活动,买了台服务器,环境什么的都弄完了,MySQL是安装的5.7的版本,连接进入的时候出现了下面的错误 这其实是MySQL5.7的一个安全机制,需要你重新设置密码. set password ...

  3. java web整合office web apps

    1.下载安装vmware虚拟机 2.下载windows server 2012或者window server 2012 R2的iso镜像 http://www.xp85.com/html/Window ...

  4. poj 2566"Bound Found"(尺取法)

    传送门 参考资料: [1]:http://www.voidcn.com/article/p-huucvank-dv.html 题意: 题意就是找一个连续的子区间,使它的和的绝对值最接近target. ...

  5. Codeforces Round #523 (Div. 2) B Views Matter

    传送门 https://www.cnblogs.com/violet-acmer/p/10005351.html 这是一道贪心题么???? 题意: 某展览馆展览一个物品,此物品有n堆,第 i 堆有a[ ...

  6. jmeter-实用插件

    1.官网下载插件管理工具 https://jmeter-plugins.org/downloads/all/ 2.将jar包放在jmeter的 lib/ext文件夹下 3.重启jmeter 4.点击“ ...

  7. 高级组件——进度条 JProgressBar

    JProgressBar pro=new JProgressBar(); pro.setIndeterminate(boolean); 设置不确定性        false,确定的进度条(显示进度, ...

  8. Java基础方法整理

    方法 9.1方法概述 方法就是用来完成解决某件事情或实现某个功能的办法 可以通过在程序代码中引用方法名称和所需的参数,实现在该程序中执行(或称调用)该方法.方法,一般都有一个返回值,用来作为事情的处理 ...

  9. python 发送邮件模板

    ##发送普通txt文件(与发送html邮件不同的是邮件内容设置里的type设置为text,下面代码为发送普通邮件的另一种方法) import smtplibimport stringfrom emai ...

  10. 使用二进制安装包的方式单机部署MySQL8.0.13

    使用二进制安装包的方式单机部署MySQL8.0.13 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 关于MySQL的介绍我这里就不多做赘述了,如何下载MySQL详情请参考:MySQ ...