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]);
}
posted @ 2018-11-29 19:40  yoyoball  阅读(255)  评论(1编辑  收藏  举报