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;
}