[ARC091E] LISDL 题解
题意:构造一个
n
n
n 的排列,满足其最长上升子序列长度为
a
a
a ,最长下降子序列长度为
b
b
b,若不存在则输出 -1
。
思路
1.边界条件:
-
a > n a>n a>n 或 b > n b>n b>n 显然无解。
-
a + b > n + 1 a+b>n+1 a+b>n+1,即最多有一个数同时在最长上升子序列和最长下降子序列中。
证明:若同时有两个数(设为 x x x, y y y)同时存在于最长上升子序列和最长下降子序列中,又因为两个数的相对位置无法改变,则 x < y x<y x<y 且 x > y x>y x>y,显然矛盾。得证。 -
a b < n ab<n ab<n, a a a 和 b b b 过小无解,这个无解的证明则与接下来的做法有关。
接下来考虑如何构造出正解。
2.尝试:
首先可以先构造出两个序列中的一个,我选择的是最长上升子序列。
以样例 5 3 2
举例:
首先尝试:
接下来需要构造最长下降子序列。
显然不能放在前面,因为
5
5
5 和
4
4
4 均大于
1
,
2
,
3
1,2,3
1,2,3 ,会导致最长下降子序列长度增大。
同样不能放在后面,同理会导致最长上升子序列长度增加。
如果加在中间:
同理,最长上升子序列长度会增加。
但是这也给了我们一些启发:
如果最开始的最长上升子序列是由
3
,
4
,
5
3,4,5
3,4,5 组成的呢?
将
1
,
2
1,2
1,2 插入中间:
此时可以发现,因为我们的上升子序列事这个排列里最大的数,不会使最长上升子序列的长度增加,并且插入的数字都比最长上升子序列里的数小,最长下降子序列也可以构造出来。并且,可以插空的位置最多只有 a a a 个,即使空中有 a a a 个数构成了上升子序列,长度也不会比 a a a 大。
当然,这种情况比较普通。
一个较大的 8 3 3
。
理解这个样例后其实做法就很清晰了。
3.做法:
其中每个在最长上升子序列中的数后都有最多 b − 1 b-1 b−1 个数组成的下降的序列,整个排列中有 a a a 个长度不大于 b b b 的下降子序列,并且至少有一个长度为 b b b 的最长下降子序列。当然,因为构造时特意用最大的 a a a 个数组成最长上升子序列,也并没有使最长上升子序列的长度因为新加入数字而增加。
这时候再看第三种无解情况:排列中最极限的情况是有 a a a 个长度为 b b b 的下降子序列组成,如果再添加数就会使两种子序列至少有一个的长度会发生变化。所以 a b < n ab<n ab<n 时,也就是最极限的情况下都无法将所有数字放进排列,就是无解。
具体细节请看代码。
4.代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,a,b;
int main(){
scanf("%d%d%d",&n,&a,&b);
if((a>n||b>n)||(a+b>=n+2)||((ll)a*b<n)){//此处a,b相乘要强转,否则会溢出
printf("-1\n");
return 0;
}
int x=0,y=n-a+1;
int cnt=0,maxn=n-a;
for(int i=1;i<=a;i++){
printf("%d ",y);//y是最长上升子序列中的数字
int p=min(maxn-cnt,b-1);
int k=x+p;
while(p--){
printf("%d ",x+p+1);
cnt++;
}
x=k,y++;
}
return 0;
}
完结撒花!