「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}的一个子集,使得这个子集中任意两 ...
随机推荐
- java锁——wait,notify,synchronized
背景:这篇博客用来总结java锁相关的知识点,平时还是要自己多加练习 wait 和 notify以及notifyAll (1).方法介绍1.wait.notify以及notifyAll都是Object ...
- Django(十七)文件上传
http://www.cnblogs.com/wupeiqi/articles/5703697.html - 文件上传 - 普通上传 - 自定义页面上传按钮 ...
- 关于Jedis是否线程安全的测试
转: 关于Jedis是否线程安全的测试 2018年09月20日 15:53:51 cwz_茶仔 阅读数:659 版权声明:转载请注明出处 https://blog.csdn.net/jk94043 ...
- C 线程学习记录
"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域. 这时的解决方法,就是在门口挂n把钥匙.进去的人就取一把钥匙,出来时再把钥匙挂 ...
- hdu 3613"Best Reward"(Manacher算法)
传送门 题意: 国王为了犒劳立下战功的大将军Li,决定奖给Li一串项链,这个项链一共包含26中珠子"a~z",每种珠子都有 相应的价值(-100~100),当某个项链可以构成回文时 ...
- sqlserver Timeout 时间已到。在操作完成之前超时时间已过或服务器未响应
随着数据库数据的不断增大,查询时间也随之增长.今天在之前一个项目中执行数据库查询超过30秒就报“Timeout 时间已到.在操作完成之前超时时间已过或服务器未响应.”了,网上找了些文章,是在.co ...
- String 中常用的几种方法
/* String(char[] value)传递字符数组 将字符数组转换为字符串 字符数组不查询编码表 */ public static void fun1(){ char[] ch = {'a', ...
- Redash 安装部署
介绍 是一款开源的BI工具,提供了基于web的数据库查询和数据可视化功能. 官网:https://redash.io/ GitHub:https://github.com/getredash/reda ...
- Thymeleaf中的&&解析问题
1.问题: 最近使用了新的html模板thymeleaf..在模板里使用js语法时遇到问题,&&不能正确的被解析,使用if(a&&b){}可以通过模板解析,但是浏览器上 ...
- docker swarm(当前官网示例)
介绍 Docker Swarm 是 Docker 公司推出的官方容器集群平台,基于 Go 语言实现 作为容器集群管理器,Swarm 最大的优势之一就是 100% 支持标准的 Docker API.各种 ...