zoukankan      html  css  js  c++  java
  • ARC114F

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

    (nle 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;
    }
    
  • 相关阅读:
    最多区间覆盖问题
    Luogu2420 让我们异或吧
    高精度计算(三) /*高精度的乘法运算*/
    用canvas画时钟
    Chrome开发者工具学习
    float浮动与清除浮动
    理解css中的position-static elativefixedabsolute
    bootstrap
    cookie与localstorage和sessionstorage的区别比较
    cookie学习
  • 原文地址:https://www.cnblogs.com/jz-597/p/14551102.html
Copyright © 2011-2022 走看看