造序列(构造,DP)

题面

在这里插入图片描述
Sample Input

7
8
7
10
31
20
100
869120

Sample Output

6
1 1 4 5 1 4
7
1 9 1 9 8 1 0
8
1 9 4 9 1 0 0 1
8
1 9 2 6 0 8 1 7
9
9 7 3 6 5 8 4 1 2
10
0 5 9 3 6 1 4 2 7 8
72
47 45 28 9 41 50 33 61 27 15 38 54 52 22 57 7 30 12 46 21 19 8 71 20 23 6 18 26 17 39 4 53 44 3 31 68 29 42 62 37 69 67 40 65 2 55 36 35 11 49 24 25 43 48 0 1 16 10 70 66 64 32 5 51 60 63 58 56 59 13 14 34

题解

首先我们想想对于一个序列怎么算它的上升子序列个数。我们一般会用 DP , d p [ i ] dp[i] dp[i] 表示以 i 结尾的上升子序列个数,那么 d p [ i ] = ∑ j = 0 i − 1 [ a j < a i ] d p [ j ]    , d p [ 0 ] = 1 dp[i]=\sum_{j=0}^{i-1}[a_j<a_i]dp[j]\;,dp[0]=1 dp[i]=j=0i1[aj<ai]dp[j],dp[0]=1,答案为 a n s [ i ] = ∑ j = 0 i d p [ j ] , a n s [ 0 ] = 1 ans[i]=\sum_{j=0}^{i}dp[j],ans[0]=1 ans[i]=j=0idp[j]ans[0]=1

考虑在序列最末端新加入一个比前面都大的数, d p [ n + 1 ] = ∑ i = 0 n d p [ i ] = a n s [ n ]    ,    a n s [ n + 1 ] = 2 ⋅ a n s [ n ] dp[n+1]=\sum_{i=0}^{n}dp[i]=ans[n]\;,\;ans[n+1]=2\cdot ans[n] dp[n+1]=i=0ndp[i]=ans[n],ans[n+1]=2ans[n],因此会使得答案乘 2——左移 1。如果在序列最末端加入一个比前面 (除了位置 0) 都小的数, d p [ n + 1 ] = d p [ 0 ] = 1    ,    a n s [ n + 1 ] = a n s [ n ] + 1 dp[n+1]=dp[0]=1\;,\;ans[n+1]=ans[n]+1 dp[n+1]=dp[0]=1,ans[n+1]=ans[n]+1,就会使得答案+1,那么就有了一个使序列长度 < 128 <128 <128 的构造方案:

  • 从 K 的最高位开始遍历,每到下一位就在序列最后加入一个比前面都大的数。
  • 如果当前位为 1,则把序列每个位置整体加 1(答案不变),然后在序列最后加入 1。

于是我们拿到了 28 分的高分。

这个构造方法实际上序列的长度为 log ⁡ 2 K + b i t c o u n t ( K ) \log_2K+bitcount(K) log2K+bitcount(K),我们会被存在过多 1 的 K 卡到序列长度 98 以上。如果我们能近乎把每一段连续的 1 都只加入 1 个点的话,由于连续的 1 不超过 log ⁡ 2 K 2 \frac{\log_2K}{2} 2log2K 段,因此可以把序列长度卡到 98 以下。怎么做呢?

我们发现,实际上不可能每一段连续的 1 都只加入一个点,但是稍微把长度卡到 98 以下是可行的。

考虑之前的 DP 计算,我们在最后加入一个数 x 时,实际上是把答案增加了 ∑ i = 0 n [ a i < x ] d p [ i ] \sum_{i=0}^{n}[a_i<x]dp[i] i=0n[ai<x]dp[i] 这么多,如果能找到这么一个 x ,使得这个值刚好为 2 ? − 1 2^?-1 2?1 不就可以优化了?

因此我们维护对于当前每一个值 x, ∑ i = 0 n [ a i < x ] d p [ i ] \sum_{i=0}^{n}[a_i<x]dp[i] i=0n[ai<x]dp[i] 是多少,当我们遍历到一段连续 n 个的 1 时,就先在序列尾部加 n 个比前面都大的数让答案左移 n 位,然后把刚好 ∑ i = 0 n [ a i < x ] d p [ i ] = 2 n − 1 \sum_{i=0}^{n}[a_i<x]dp[i]=2^n-1 i=0n[ai<x]dp[i]=2n1 的 x 加入序列尾部,最后把序列前面 ≥ x 的数都+1。这样就能比较好地优化长度了,实测拍了20000+组 T=1500 的数据,序列长度没有超过 89 的。

CODE

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 205
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#pragma GCC optimize(2)
LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f * x;
}
const int MOD = 998244353;
int n,m,i,j,s,o,k;
int a[MAXN],L,hb,mx;
LL ct[MAXN];
int mp(LL x) {
	for(int i = 0;i <= mx;i ++) if(ct[i] == x) return i;
	return -1;
}
void addbot(int ad) {
	for(int i = 1;i <= L;i ++) if(a[i] > ad) a[i] ++;
	mx ++;
	for(int i = mx;i > ad+1;i --) swap(ct[i],ct[i-1]);
	a[++ L] = ad+1; ct[ad+1] = ct[ad];
	for(int i = ad+1;i <= mx;i ++) ct[i] += ct[ad];
	return ;
}
int main() {
	freopen("make.in","r",stdin);
	freopen("make.out","w",stdout);
	int T = read();
	while(T --) {
		LL K = read();
		L = 0;
		memset(a,0,sizeof(a));
		memset(ct,0,sizeof(ct));
		hb = 62,mx = 0;
		ct[0] = 1ll;
		while(!(K & (1ll<<hb))) hb --;
		for(int i = hb-1;i >= 0;i --) {
			if(K & (1ll<<i)) {
				LL tm = 1ll;
				while(i > 0 && (K & (1ll<<(i-1)))) {
					i --; tm = tm<<1ll|1ll;
				}
				for(LL tmp = tm;tm > 0ll;tmp = min(tmp,tm)) {
					tmp = tm;
					int add = 0;
					while((add=mp(tmp)) < 0) tmp >>= 1ll;
					LL tmc = tmp;
					while(tmc > 0) {
						a[++ L] = ++ mx; ct[mx] = ct[mx-1]<<1ll;
						tm >>= 1ll;
						tmc >>= 1ll;
					}
					addbot(add);
				}
			}
			else {
				a[++ L] = ++ mx;
				ct[mx] = ct[mx-1]<<1ll;
			}
		}
		printf("%d\n",L);
		for(int i = 1;i <= L;i ++) printf("%d ",a[i]);
		ENDL;
	}
	return 0;
}
posted @ 2021-04-01 15:24  DD_XYX  阅读(99)  评论(0编辑  收藏  举报