Codeforces 582B Once Again... LIS变形

本文介绍了一种高效算法,用于解决给定数组拼接后的最长非递减子序列问题。通过对输入数组特性分析,避免了直接合并带来的计算复杂度问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


B. Once Again...
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given an array of positive integers a1, a2, ..., an × T of length n × T. We know that for any i > n it is true that ai = ai - n. Find the length of the longest non-decreasing sequence of the given array.

Input

The first line contains two space-separated integers: nT (1 ≤ n ≤ 1001 ≤ T ≤ 107). The second line contains n space-separated integers a1, a2, ..., an (1 ≤ ai ≤ 300).

Output

Print a single number — the length of a sought sequence.

Examples
input
4 3
3 1 4 2
output
5
Note

The array given in the sample looks like that: 3, 1, 4, 23, 1, 4, 2, 3, 1, 4, 2. The elements in bold form the largest non-decreasing subsequence.


    题意:给出一个初始的字符串,整个字符串是t个这样的字符串拼接。求出这样的非递减字符串的最大长度。

    分析:t的上限是10的7次方,如果直接合并数组然后套LIS 模板的话肯定不行。所以这边可以分析,因为n的范围不大,在k不大于n的时候可以直接套LIS模板,当k的范围特别大的时候,由于整个大的数组是一片一片的,每一片都是初始输入的数组,将整个大的数组分成两个部分,一个是n个n数组求取最大非递减序列,另一个部分中间的数组选出的都是一个元素,插空拼接在整个大的数组中间。如例一:数组中出现的元素的个数都是1,最大个数的元素可以是2,最大子序列就是12234。实在不理解,可以将k的数据放小,找出规律即可。找出n数组中最大个数的元素,重复插空在中间。见AC代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
//map <int ,int> m;
const int maxn=10005;
int a[maxn],b[maxn],flag[305],dp[maxn];
int main()
{
	int n,t;
	while(~scanf("%d %d",&n,&t))
	{
		int k=1;
		memset(flag,0,sizeof(flag));
		memset(dp,0,sizeof(dp));
		for(int i=1; i<=n; i++)
		{
			scanf("%d",&a[i]);
			flag[a[i]]++;
		}
		int nummax=0,num=0;
		for(int i=1; i<=305; i++)
			if(flag[i]>nummax)
			{
				nummax=flag[i]; //找到最多个数的数的个数 
				num=i;//找到最多个数的个数 
			}
		for(int i=0; i<min(n,t); i++)
			for(int j=1; j<=n; j++)
				b[k++]=a[j];
		dp[1]=b[1]; //模板 
		int maxlen=1;
		for(int i=2; i<=k; i++)
		{
			int l=0;
			int r=maxlen,mid;
			if(dp[maxlen]<=b[i])//若此时最大长度的子序列的最小值 仍小于a[i] 则最大长度加一
				dp[++maxlen]=b[i];
			else
			{
				r=maxlen;
				while(l<=r)
				{
					mid=(l+r)/2;
					if(dp[mid]<=b[i])
						l=mid+1;
					else
						r=mid-1;
				}
				//找到第一个大于a[i]的数 更新其的值 使其值更小
				dp[l]=b[i];
			}
		}
	//	printf("num=%d nummax=%d\n",maxlen,nummax);
		if(t > n) //在原来的基础上  将数目最多的数插空进去 
			maxlen += (t - n) * nummax;
		printf("%d\n",maxlen);
	}
}

    刷题长见识……

    特记下,以备后日回顾。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值