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;
}