AGC064_A i i's

AGC064_A i i's

题目传送门

题意

给定\(n\),请构造一个长度为\(n*(n+1)/2\)的序列, 使得\(1,2,3...n\)中的任意一个数 \(x\) 刚好出现\(x\)次,

并且满足相邻两个数差的绝对值要么是1, 要么是2。 (第一个数和最后一个也相邻)

题解

不错的一道题, 思维, 构造, 子问题化巧妙融合,当然不排除我想复杂了。

我们考虑递推或者说dp的思想, 如何在一个n的答案序列中插入若干个数,形成n+1的答案。

我们不妨让原序列中的每个数都加一, 不难发现此时只需插入1到n+1每个数各一次即可。

如果只是把1到n+1插到末尾,那是很好插的,关键是要考虑插入部分开头和原有部分末尾的衔接,以及插入部分末尾和原有部分开头的衔接。

这样看来,状态里没有开头末尾是不行的,但也不能暴力加状态, 还是回到构造里来。

考虑n=3时的一种答案, 1 3 2 3 2 3 , 我们考虑能不能改成求n时,以1开头,3结尾的答案序列。

可以。

首先我们来看如何求解1-n每个数出现一次时答案(而不是x数出现x次): 我们可以从1开始,每次往后跳两个,跳到n或者n-1时,再倒过来把没有跳过的跳一边,这样就可以得到一个1-n的以1开头,2结尾的合法序列

比如 1,2, 3,4 ,5,6 , 跳的顺序是1, 3, 5, 6, 4, 2 , 相差不过2。

于是我们回到刚刚的构造里来:

若现在有n时以1开头3结尾的答案序列 :

1.......3

开始构造n+1时的答案

我们使其每个数加一变成:

2.......4

开头要是1, 我们往开头插入1, 3因为要放在末尾,先预留着。

2还没有用,很不爽,干脆插4后面得了,于是有:

1 2.......4 2

接下来要插入 4 - n+1 ,用上文说过的方法插入,可以得到4开头,5结尾的插入序列, 有:

1 2.......4 2 4........5

插入预留的3 , 就构造出n+1 时的1开头3结尾答案序列

这只是a题啊, 菜死我了快复健


实现

复健代码很丑见谅

#include <iostream>
#include <cstdio>
using namespace std;

const int N = 1005;
int vis[N];
int ans[N*N*2];

int main(){
  	int n; scanf("%d", &n);
  	int m = 6;
  	ans[1] = 1;
  	ans[2] = 3;
  	ans[3] = 2;
  	ans[4] = 3;
  	ans[5] = 2;
  	ans[6] = 3;
	
	for(int i=4; i<=n; i++){
		for(int j=m+1; j>=2; j--) ans[j] = ans[j-1] + 1;
		ans[1] = 1;
		m++;
		ans[++m] = 2;
		for(int j=4; j<=i; j++) vis[j] = 0;
		for(int j=4; j<=i; j+=2) vis[j]=1, ans[++m]=j;
		for(int j=i; j>=5; j--) {
			if(vis[j]) continue;
			ans[++m] = j;
		}
		ans[++m] = 3;
	} 
	
	for(int i=1; i<=m; i++)	printf("%d ", ans[i]);
 
  	return 0;
}
posted @ 2023-09-04 14:26  ltdJcoder  阅读(11)  评论(0编辑  收藏  举报