【2020 杭电多校】第7场 Increasing and Decreasing 构造
题意
给出 \(n\) , \(x\) , \(y\) ,问是否可以构造出一个长度为 \(n\) 的全排列,满足以下条件:
- 最长递增子序列的长度等于 \(x\) ,
- 最长递减子序列的长度等于 \(y\)
思路
构造 \(x\) 个块,每个块里的数字都是递减的,并且最长的块长度要为 \(y\) ,由这个最长的块构成最长递减子序列。
只需第 \(i\) 块的最大值小于第 \(i+1\) 块的最小值,最长递增子序列由这 \(x\) 个块中的任意 \(x\) 个元素组成。
因为要保证字典序最小,尽可能使得前面的块长度为 \(1\) ,并且第 \(i\) 块只有 \(i\) 一个元素,也就是后面块的长度尽可能的长。
考虑极限情况,除了最后一个长度为 \(y\) 的块,其他块的长度都为 \(1\) ,那么需要 \(x-1+y\) 个数字
所有的块长度都为 \(y\) ,需要 \(x\times y\) 个数字。
所以当 \(x+y-1 \leq n \leq x \times y\) 时才可以构造成功。
代码
#include <bits/stdc++.h>
#define fuck system("pause")
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;
int tmp[N];
stack<int> s;
int main()
{
int _;
scanf("%d", &_);
while (_--) {
int n, x, y;
scanf("%d%d%d", &n, &x, &y);
if (n < x + y - 1 || n > 1LL * x * y) {
printf("NO\n");
continue;
}
printf("YES\n");
int cnt = 0;
while (n > 0) {
x--, tmp[++(cnt=0)] = n--;//拿出一个元素放到当前块中
while (n > x && cnt < y && n > 0) {//剩下的元素大于剩下块的个数,接着往当前块填
tmp[++cnt] = n--; //直到当前块的元素个数等于 y
}
for (int i = cnt; i ; i--) {
s.push(tmp[i]);
}
}
while (!s.empty()) {
printf("%d%c", s.top(), s.size() == 1 ? '\n' : ' ');
s.pop();
}
}
return 0;
}
/*
*/