• 多校联测11 8ady


    题目大意

    有一个排列 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an,我们现在进行如下操作:

    for(int i=1;i<=n-m+1;i++) sort(a+i,a+i+m);
    
    • 1

    设最后的结果为 b 1 , b 2 , ⋯   , b n b_1,b_2,\cdots,b_n b1,b2,,bn,求满足条件的 a a a中字典序第 k k k小的 a a a

    1 ≤ n ≤ 1 0 6 , m ≤ n , 1 ≤ k ≤ 1 0 18 1\leq n\leq 10^6,m\leq n,1\leq k\leq 10^{18} 1n106,mn,1k1018 b i b_i bi 1 1 1 n n n的一个排列。


    题解

    由题意可得 b i b_i bi a 1 , a 2 , … , a min ⁡ ( i + m − 1 , n ) a_1,a_2,\dots,a_{\min(i+m-1,n)} a1,a2,,amin(i+m1,n)中,满足不在 b 1 , b 2 , … , b i − 1 b_1,b_2,\dots,b_{i-1} b1,b2,,bi1中的最小的数。

    那么,当 b i − 1 > b i b_{i-1}>b_i bi1>bi,就一定有 a i + m − 1 = b i a_{i+m-1}=b_i ai+m1=bi

    把这些已经确定的 b i b_i bi去掉,剩下的问题等价于 n n n变小, m , k m,k m,k不变, b i = i b_i=i bi=i的子问题。

    下面,我们假设 b i = i b_i=i bi=i

    考虑从小到大将每一个 i i i填入 a a a中,易得 i i i需要填在 [ 1 , min ⁡ ( i + m − 1 , n ) ] [1,\min(i+m-1,n)] [1,min(i+m1,n)]中任意没填过的位置,那么填 i i i的方案数为 min ⁡ ( m , n − i + 1 ) \min(m,n-i+1) min(m,ni+1)

    然而,因为方案数很大,而 k k k相对没那么大,所以 a a a有一个前缀都满足 a i = i a_i=i ai=i

    f i f_i fi表示满足上面条件的前缀为 n − i n-i ni,也就是后面有最多 i i i个数不满足,那么

    f i = f i − 1 × min ⁡ ( m , i ) f_i=f_{i-1}\times \min(m,i) fi=fi1×min(m,i)

    m = 1 m=1 m=1时,显然只有一个答案。因为保证有解,这种情况下 k = 1 k=1 k=1

    m > 1 m>1 m>1时, f i ≥ 2 i − 1 f_i\geq 2^{i-1} fi2i1,可得 f 62 ≥ k f_{62}\geq k f62k

    那么,我们只需取最后 min ⁡ ( n , 62 ) \min(n,62) min(n,62)个位置,后面用一个状压维护状态,再枚举每一位填什么即可。细节见代码。

    时间复杂度为 O ( n + v 3 ) O(n+v^3) O(n+v3),其中 v = min ⁡ ( n , 62 ) v=\min(n,62) v=min(n,62)

    code

    #include
    using namespace std;
    int n,m,a[1000005],b[1000005],c[1000005],num[1000005],z[1000005];
    long long k;
    long long gt(int t,int w,long long now){
    	long long re=1;
    	for(int i=1;i<=t;i++){
    		if((now>>i-1)&1){
    			if(re>k/(min(i+m-1,t)-w)+1) return k+1;
    			re=re*(min(i+m-1,t)-w);
    			if(re>k) return k+1;
    			++w;
    		}
    	}
    	return re;
    }
    void dd(int l,int t){
    	long long now=(1ll<<t)-1;
    	for(int i=1;i<=l-t;i++) c[i]=num[i];
    	for(int i=t;i>=1;i--){
    		for(int j=1;j<=t;j++){
    			if((now>>j-1)&1){
    				long long vt=gt(t,t-i+1,now^(1ll<<j-1));
    				if(k>=vt) k-=vt;
    				else{
    					c[l-i+1]=num[l-t+j];
    					now^=(1ll<<j-1);
    					break;
    				}
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d%d%lld",&n,&m,&k);--k;
    	for(int i=1;i<=n;i++){
    		scanf("%d",&b[i]);
    	}
    	int lst=b[1],cnt=0;
    	for(int i=2;i<=n;i++){
    		if(lst>b[i]){
    			a[i+m-1]=b[i];
    			z[b[i]]=1;
    		}
    		else lst=b[i];
    	}
    	for(int i=1;i<=n;i++){
    		if(!z[i]) num[++cnt]=i;
    	}
    	dd(cnt,min(cnt,62));
    	int now=1;
    	for(int i=1;i<=n;i++){
    		if(!a[i]){
    			a[i]=c[now];++now;
    		}
    		printf("%d ",a[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
  • 相关阅读:
    邮件营销方案
    基于AI智能识别技术的智慧展览馆视频监管方案设计
    Java 中代码优化的 30 个小技巧(上)
    工作中整理的常用的Linux命令
    第十四届蓝桥杯第一期模拟赛试题与题解 C++
    hub.docker访问不了的问题(一步解决)
    为了7K ,离开字节,值得吗....
    SPL工业智能:发现时序数据的异常
    基于webservice 使用HttpClient调用
    快速上手Django(六) -Django之Django drf 序列化器Serializer类
  • 原文地址:https://blog.csdn.net/tanjunming2020/article/details/133690521