AtCoder Grand Contest 012 C

感觉APIO2022的最后一题就是从这题改来的,简化了一些后又加上了一些奇怪的优化。

首先我们将这题转成和APIO2022一样的格式——如果我们将最终答案分为两个部分,前半部分有\(n\)个上升子序列,并且值域在\(1\)\(m\),后半部分是\(1,2,3,...,m\)的序列。不难发现这个构造出的序列一定有\(n\)个符合题目要求的子序列。

比如\(n=7\)时,有:

1 2 3 1 2 3

那么前半部分有\(1,2,3,12,13,23,123\)\(7\)个上升子序列,后半部分也有唯一对应的相等串,比如\(2323\)

再比如\(n=13\)时,有

3 2 4 1 5 1 2 3 4 5

前半部分有\(3,2,4,1,5,24,34,25,35,45,15,245,345\)\(13\)个上升子序列,后半部分也有对应的相等串,比如\(345345\)

至此,我们就将题面转化为:

构造一个长度不超过\(100\)(因为后面我们还要重复一遍)的数列,使得上升子序列个数为\(n\)

这里我们暂且认为空子序列也是上升的,也就是我们要求出有\((n+1)\)个上升子序列的数列。

那么不难发现,如果我们当前有一个数列\(A\),如果我们加入\(\min A -1\),则上升子序列个数\(+1\);加入\(\max A +1\),则上升子序列个数\(\times 2\)

故我们可以将\((n+1)\)二进制分解,从而得到每一处位置是加入一个最小值还是最大值,从而求出整个数列。

这样,我们使用的数不超过\(\log n\),数列的长度不超过\(2\log n\),可以AC。

#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl

using ll=long long;
ll n,id,num,a[205];
std::vector<ll> tp,smallest,biggest;

int main() {
	scanf("%lld",&n); n++;
	ll dig=0,temp=1; while(temp<=n) dig++,temp<<=1ll; dig--;
	for(int i=1;i<=dig;i++) tp.push_back(0);
	ll p=(ll)tp.size(),d=0;
	while(p>0) {
		if(n&(1ll<<d)) tp.insert(tp.begin()+p,1);
		p--; d++;
	}
	for(auto item : tp) {
		if(item) smallest.push_back(++id);
		else biggest.push_back(++id);
	}
	for(int i=(int)smallest.size()-1;~i;i--) a[smallest[i]]=++num;
	for(int i=0;i<(int)biggest.size();i++) a[biggest[i]]=++num;
	for(int i=1;i<=num;i++) a[++id]=i;
	printf("%d\n",id); for(int i=1;i<=id;i++) printf("%d ",a[i]);
	return 0;
}
posted @ 2022-07-07 18:56  Nastia  阅读(31)  评论(0)    收藏  举报