题目链接


\(Description\)

给定一个长为n的序列,每次可以反转 \([l,r]\) 区间,代价为 \(r-l+1\)。要求在\(4*10^6\)代价内使其LIS长度最长,并输出需要操作的数量及每个反转操作。

\(n\leq32000\)。

\(Solution\)

显然,需要在4e6的代价内将\(1\sim n\)尽可能排好序。(而且不一定要反转一大段区间,可以交换相邻元素实现一个元素的移动)

Subtask2 \(n\leq1000\)

可以用冒泡排序将每个元素放到应放的位置上,每次交换相邻元素,代价\(O(1)\),总复杂度\(O(\frac{n(n-1)}{2})\),代价与复杂度相同

可以看出,程序排序所消耗时间可近似看做代价

Subtask3 值域\([0,5]\)

考虑值域仅为\([0,1]\)时应怎么做,序列是由几堆\(00,11\)构成的,用01归并排序 每次将一堆\(0\)与左边的一堆\(1\)互换位置

最后形成\(00001111\)这样的序列,这样可以把\(0,1\)区分出来。01归并排序自带\(1/2\)常数

每次区分两个不同数,一共需要\(5\)次,复杂度/代价为 \(O(2.5nlogn)\)

Total

参考上一个做法,对所有数字进行分治,选择一个值\(mid\),将\(\geq mid\)的数设为\(0\),\(<mid\)的数设为\(1\),每次01归并排序可以区分两堆数。

分治在\(O(\log n)\)层一定会结束。可以按照二进制从高位到低位进行分治。

复杂度/代价为 \(O(0.5nlog^2n)\),满打满算 \(3.6*10^6\)


[Update]

另外有一种贪心,就是每次交换相邻的两段\(01\),比如:10101010->01010101->00101011...。直接这样复杂度是\(n^2\)的。

考虑仍是每次交换相邻的两段\(01\),但间隔一段\(01\):10101010->01100110->00011110->00001111。可以发现开头\(0\)的个数呈几何增长(我没发现),所以复杂度\(O(n\log n)\)。但是应该比上面那个难写...但是这个思想应该(可能)很常用。


#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#define gc() getchar()
#define pr std::pair<int,int>
#define mp std::make_pair
const int N=32010; int n,A[N];
bool B[N];
std::vector<pr> ans; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
void Rev(int l,int r)
{
std::reverse(A+l,A+r+1), std::reverse(B+l,B+r+1);
ans.push_back(mp(l,r));
}
void Solve_01(int l,int r)//将当前[l,r]的01归并排序
{
if(l==r) return;
int mid=l+r>>1;
Solve_01(l,mid), Solve_01(mid+1,r);//将左右子区间化为有序(0011)
int p1=0,p2=0;//现在[l,r]为0011,0011,找到左边最左的1,右边最右的0,将这一区间交换
//[l,r] (0)1,0(1)也是有可能的
for(int i=l; i<=mid; ++i)
if(B[i]) {p1=i; break;}
for(int i=r; i>mid; --i)
if(!B[i]) {p2=i; break;}
if(p1 && p2) Rev(p1,p2);
}
void Solve_Num(int l,int r,int d)//将当前[l,r]的数按照第d位分为0/1,记录在B中
{
if(l>r||d==-1) return;
for(int i=l; i<=r; ++i) B[i]=(A[i]>>d)&1;//按照第d位是否为1分01
Solve_01(l,r);//以当前01归并
int p=r;//p为第一个1的位置(如果有)
//01归并后的序列有三种情况:1.0000;2.1111;3.000111
//对于1.2显然继续[l,r],d-1的分治排序即可,对于3,只需对0,1的区间分别进行归并即可
for(int i=l; i<=r; ++i)
if(B[i]) {p=i-1; break;}
Solve_Num(l,p,d-1), Solve_Num(p+1,r,d-1);
} int main()
{
n=read();
for(int i=1; i<=n; ++i) A[i]=read();
Solve_Num(1,n,14);
printf("%d\n",ans.size());
for(int i=0; i<ans.size(); ++i) printf("%d %d\n",ans[i].first,ans[i].second); return 0;
}

洛谷.T22136.最长不下降子序列(01归并排序 分治)的更多相关文章

  1. 洛谷P2766 最长不下降子序列问题(最大流)

    传送门 第一问直接$dp$解决,求出$len$ 然后用$f[i]$表示以$i$为结尾的最长不下降子序列长度,把每一个点拆成$A_i,B_i$两个点,然后从$A_i$向$B_i$连容量为$1$的边 然后 ...

  2. 洛谷 P2766 最长不下降子序列问【dp+最大流】

    死于开小数组的WA?! 第一问n方dp瞎搞一下就成,f[i]记录以i结尾的最长不下降子序列.记答案为mx 第二问网络流,拆点限制流量,s向所有f[i]为1的点建(s,i,1),所有f[i]为mx(i+ ...

  3. 洛谷 [P2766] 最长不下降子序列问题

    啊啊啊,再把MAXN和MAXM搞反我就退役 层次图求不相交路径数 第一问简单DP 第二问想办法把每一个不上升子序列转化成DAG上的一条路径,就转换成了求不相交路径数 因为每一个数只能用一次,所以要拆点 ...

  4. 洛谷P2766 最长不下降子序列问题 网络流_DP

    Code: #include<cstdio> #include<iostream> #include<vector> #include<algorithm&g ...

  5. 【动态规划】【二分】【最长不下降子序列】洛谷 P1020 导弹拦截

    最长不下降子序列的nlogn算法 见 http://www.cnblogs.com/mengxm-lincf/archive/2011/07/12/2104745.html 这题是最长不上升子序列,倒 ...

  6. 洛谷P2766 最长递增子序列问题

    https://www.luogu.org/problemnew/show/P2766 注:题目描述有误,本题求的是最长不下降子序列 方案无限多时输出 n 网络流求方案数,长见识了 第一问: DP 同 ...

  7. 最长不下降子序列(LIS)

    最长上升子序列.最长不下降子序列,解法差不多,就一点等于不等于的差别,我这里说最长不下降子序列的. 有两种解法. 一种是DP,很容易想到,就这样: REP(i,n) { f[i]=; FOR(j,,i ...

  8. 最长不下降子序列 O(nlogn) || 记忆化搜索

    #include<stdio.h> ] , temp[] ; int n , top ; int binary_search (int x) { ; int last = top ; in ...

  9. tyvj 1049 最长不下降子序列 n^2/nlogn

    P1049 最长不下降子序列 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 求最长不下降子序列的长度 输入格式 第一行为n,表示n个数第二行n个数 输出格式 ...

随机推荐

  1. Ubuntu Eclipse C++运行问题:launch failed.Binary not found

    在Ubuntu下的Eclipse C++环境出现launch failed.Binary not found问题时,可采用如下解决方案: (1)首先检查系统中是否成功安装g++.如果console输出 ...

  2. 使用zabbix3.0.4的ICMP Ping模版实现对客户端网络状态的监控

    一.登陆Zabbix服务器做以下操作: 1.fping安装 wget http://www.fping.org/dist/fping-3.16.tar.gz tar zxvf fping-3.16.t ...

  3. Nodejs实现WebSocket通信demo

    一.创建websocket.js文件 步骤: 1.创建websocket.js文件,复制如下代码: 2.安装nodejs-websocket依赖: 3.该文件夹下命令行执行 node websocke ...

  4. PYTHON-组合 封装 多态 property装饰器

    # 组合'''软件重用的重要方式除了继承之外还有另外一种方式,即:组合组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合 1. 什么是组合 一个对象的属性是来自于另外一个类的对象,称之 ...

  5. asp.net core 操作误区

    更新时提示数据变化错误 在更新事件中提示下面错误,在网上找了一下,大部分都是说是冲突问题,但是测试时同时只有一个客户端在进行操作,不应该会有冲突问题,后来发现编辑加载时的ID,和更新提交时的ID不同了 ...

  6. Oracle 11g安装步骤以及Oracle11g创建表空间和用户,并授权

    Oracle 11g安装步骤详解 一.Oracle 下载 注意Oracle分成两个文件,下载完后,将两个文件解压到同一目录下即可. 路径名称中,最好不要出现中文,也不要出现空格等不规则字符. 官方下地 ...

  7. dispatchers 设置

    Oracle连接方式(dispatchers 设置) oracle 响应客户端请求有两种方式: 1 专有连接:用一个服务器进程响应一个客户端请求 2 共享连接:用一个分派器(dispatcher)响应 ...

  8. hdu 1542 线段树+扫描线 学习

    学习扫描线ing... 玄学的东西... 扫描线其实就是用一条假想的线去扫描一堆矩形,借以求出他们的面积或周长(这一篇是面积,下一篇是周长) 扫描线求面积的主要思想就是对一个二维的矩形的某一维上建立一 ...

  9. python 全栈开发,Day13(迭代器,生成器)

    一.迭代器 python 一切皆对象 能被for循环的对象就是可迭代对象 可迭代对象: str,list,tuple,dict,set,range 迭代器: f1文件句柄 dir打印该对象的所有操作方 ...

  10. ERP产品销售发货判断库存功能(四十二)

    产品数量的前端(键盘抬起的事件): <td> <input type="text" name="proCount" onkeyup=" ...