[ARC091E] LISDL 题解

[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 举例:
首先尝试:
样例解释1

接下来需要构造最长下降子序列。
显然不能放在前面,因为 5 5 5 4 4 4 均大于 1 , 2 , 3 1,2,3 1,2,3 ,会导致最长下降子序列长度增大。
同样不能放在后面,同理会导致最长上升子序列长度增加。
如果加在中间:
样例解释2

同理,最长上升子序列长度会增加。
但是这也给了我们一些启发:
如果最开始的最长上升子序列是由 3 , 4 , 5 3,4,5 3,4,5 组成的呢?
1 , 2 1,2 1,2 插入中间:
样例解释4

此时可以发现,因为我们的上升子序列事这个排列里最大的数,不会使最长上升子序列的长度增加,并且插入的数字都比最长上升子序列里的数小,最长下降子序列也可以构造出来。并且,可以插空的位置最多只有 a a a 个,即使空中有 a a a 个数构成了上升子序列,长度也不会比 a a a 大。

当然,这种情况比较普通。
一个较大的 8 3 3

5

理解这个样例后其实做法就很清晰了。

3.做法:

其中每个在最长上升子序列中的数后都有最多 b − 1 b-1 b1 个数组成的下降的序列,整个排列中有 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;
}

完结撒花!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值