LCIS
### Description
给你一个长度为\(n\)的整数序列,你可以进行的操作是\(rev(l,r)\)表示花费\(r-l+1\)的代价将\([l,r]\)区间内的数字翻转,现在要求在\(4*10^6\)的代价内最大化最终序列的最长上升子序列长度,输出任意一种构造方案
数据范围:\(n<=32000\),\(0<=\)序列中的数字\(<=32000\)
Solution
比较直白的想法就是你如果把整个序列排个序自然就满足LIS条件了,那么现在考虑构造一种满足代价要求的方案
这里有一种很妙的想法,考虑一个类似快排的处理方式:首先离散化(注意每个数都给一个不同的新值)然后我们分治来做,对于当前区间,我们将该区间的离散化后的中位数作为一个标准值,然后剩下来的数可以分为两类,小于标准值的和大于标准值的,我们将小于标准值的全部丢到\(mid\)前面,将大于标准值的全部丢到\(mid\)后面,递归处理完之后前半部分也满足靠后的是大于标准值的,后半部分靠前的是小于标准值的(如果有的话),那么这个时候我们就直接找到前后分界的两个位置然后\(rev\)一下就可以完成大于标准值和小于标准值两类数的分离,然后我们再用同样的方式处理\([l,mid-1]\)和\([mid+1,r]\)即可
至于贡献的话。。好像01序列排序的交换次数(只能交换相邻位置)是有保障的,然后我们在处理的时候两类数的分离其实就是\(01\)分离,所以可以保证到总代价不超
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=32010;
int a[N],rec[N*14][2],tmp[N];
int n,m,ans,val;
void rev(int l,int r){
if (l>=r) return;
rec[++ans][0]=l; rec[ans][1]=r;
reverse(a+l,a+1+r);
}
void solve1(int l,int r){
if (l>=r) return;
int mid=l+r>>1,L,R;
solve1(l,mid);
solve1(mid+1,r);
L=r; R=l;
for (int i=l;i<=r;++i)
if (a[i]>=val){L=i; break;}
for (int i=r;i>=l;--i)
if (a[i]<=val){R=i; break;}
rev(L,R);
}
void solve(int l,int r){
if (l>=r) return;
int mid=l+r>>1,L,R;
for (int i=l;i<=r;++i) tmp[i]=a[i];
sort(tmp+l,tmp+1+r);
val=tmp[mid];
for (int i=l;i<=r;++i){
if (a[i]==val){
rev(min(i,mid),max(i,mid));
break;
}
}
solve1(l,mid-1);
solve1(mid+1,r);
L=R=mid;
for (int i=l;i<=r;++i)
if (a[i]>=val){L=i; break;}
for (int i=r;i>=l;--i)
if (a[i]<=val){R=i; break;}
rev(L,R);
solve(l,mid-1);
solve(mid+1,r);
}
bool cmp(int x,int y){return a[x]<a[y];}
void prework(){
for (int i=1;i<=n;++i) tmp[i]=i;
sort(tmp+1,tmp+1+n,cmp);
for (int i=1;i<=n;++i) a[tmp[i]]=i;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",a+i);
prework();
solve(1,n);
printf("%d\n",ans);
for (int i=1;i<=ans;++i) printf("%d %d\n",rec[i][0],rec[i][1]);
}