ARC114F - Permutation Division

一个排列,你要将其划分成恰好\(K\)段,使得每段分别作为整体,然后进行重排列之后得到的新排列的最大字典序最小。

\(n\le 2*10^5\)


首先有\(O(poly(n))\)的做法:按位确定,问题变成了判断一段前缀是否可行。

知道这个前缀,有些分界点是可以确定的,于是先划分成几个大段。除了最后一段外,每个大段求个最长下降子序列;最后一段需要枚举哪个点\(x\)作为最后一段的分界点,这个点前面做最长下降子序列,然后算不在前缀的部分最多能加多少贡献(即能多划分多少段):首先不在前缀中的已经分成若干段,每段开头是一定有的,要求它们小于\(x\),并且对于其它的小于\(x\)且并不在每段开头的点,它们都可以产生贡献。如果总贡献大于等于\(K\)则可行。

这个东西应该能强行数据结构维护,但是细节巨多,表示刚了一个早上没有刚出来。

其实还有条性质:最终的答案要和原排列的公共前缀尽量长。因为答案一定比原排列大,所以公共前缀越长,两者相差就越小。

如果求出了最长公共前缀\(len\),后面的是可以贪心做的。

这个做法和上一个的区别,主要是不需要考虑划分成若干段,因为只有一段。于是简单很多。设\(f_x\)表示在\(p\)中以\(p_x\)结尾的最长下降子序列的长度,\(query(len,x)=\sum_{i>len}[p_i<x]\),那么如果存在\(x\)满足\(f_x+query(len,x)\ge K\),则\(len\)合法,可以继续扩展。

\(len\)扩展到不能扩展为止,然后找到\(x\)满足\(f_x\)最大,并且\(f_x+query(len,x)\ge K\)。于是把\(len\)后面的分成\(K-f_x\)段,取\(p_{len+1}\)以及另外前\(K-f_x-1\)小的作为段头即可。


using namespace std;
#include <bits/stdc++.h>
#define N 200005
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
#define INF 1000000000
int n,K;
int p[N];
int s[N*4],t[N*4];
void upd(int k){
	s[k]=s[k<<1]+s[k<<1|1];
	t[k]=max(t[k<<1],s[k<<1]+t[k<<1|1]);
}
void init(int k=1,int l=1,int r=n){
	if (l==r){
		s[k]=1;
		t[k]=-INF;
		return;
	}
	int mid=l+r>>1;
	init(k<<1,l,mid);
	init(k<<1|1,mid+1,r);
	upd(k);
}
void modify(int x,int c,int v,int k=1,int l=1,int r=n){
	if (l==r){
		s[k]=c;
		t[k]=v;
		return;
	}
	int mid=l+r>>1;
	if (x<=mid) modify(x,c,v,k<<1,l,mid);
	else modify(x,c,v,k<<1|1,mid+1,r);
	upd(k);
}
int query(int st,int en,int k=1,int l=1,int r=n){
	if (st<=l && r<=en)
		return s[k];
	int mid=l+r>>1,res=0;
	if (st<=mid) 
		res+=query(st,en,k<<1,l,mid);
	if (mid<en) res+=query(st,en,k<<1|1,mid+1,r);
	return res;
}
struct TA{
	int t[N];
	void ins(int x,int c){
		x=n-x+1;
		for (;x<=n && c>t[x];x+=x&-x)
			t[x]=c;
	}
	int query(int x){
		x=n-x+1;
		int r=-INF;
		for (;x;x-=x&-x)
			r=max(r,t[x]);
		return r;
	}
} ta;
int f[N];
pair<int,int> ls[N];
int main(){
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	scanf("%d%d",&n,&K);
	for (int i=1;i<=n;++i)
		scanf("%d",&p[i]);
	if (p[1]<K){
		for (int i=n,nxt=n+1;i>=1;--i)
			if (p[i]<=K)
				ls[p[i]]=mp(i,nxt-1),nxt=i;
		for (int i=n;i>=1;--i)
			if (ls[i].fi)
				for (int j=ls[i].fi;j<=ls[i].se;++j)
					printf("%d ",p[j]);
		return 0;
	}
	f[1]=1;
	for (int i=1;i<=n;++i)
		ta.t[i]=-INF;
	ta.ins(p[1],1);
	for (int i=2;i<=n;++i){
		f[i]=ta.query(p[i])+1;
		ta.ins(p[i],f[i]);
	}
	init(1,1,n);
	modify(p[1],0,f[1]);
	int len=1;
	for (int i=2;i<=n;++i){
		modify(p[i],0,f[i]);
		if (t[1]<K){
			modify(p[i],1,-INF);	
			break;	
		}
		len++;
	}
	int mx=0;
	for (int i=1;i<=len;++i)
		if (f[i]+query(1,p[i])>=K)
			mx=max(mx,f[i]);
	K-=mx;
	static pair<int,int> q[N];
	int nq=0;
	for (int i=len+2;i<=n;++i)
		q[++nq]=mp(p[i],i);
	sort(q+1,q+nq+1);
	q[0]=mp(p[len+1],len+1);
	static int bz[N];
	for (int i=0;i<K;++i)
		bz[q[i].se]=1;
	for (int i=n,nxt=n+1;i>len;--i)
		if (bz[i])
			ls[p[i]]=mp(i,nxt-1),nxt=i;
	for (int i=1;i<=len;++i)
		printf("%d ",p[i]);
	for (int i=n;i>=1;--i)
		if (ls[i].fi)
			for (int j=ls[i].fi;j<=ls[i].se;++j)
				printf("%d ",p[j]);
	return 0;
}
posted @ 2021-03-17 18:38  jz_597  阅读(123)  评论(0编辑  收藏  举报