Codeforces Round #323 (Div. 1) B. Once Again... 最长非严格递增子序列

108 篇文章 0 订阅
24 篇文章 0 订阅

B. Once Again…
time limit per test1 second
memory limit per test256 megabytes
inputstandard input
outputstandard 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: n, T (1 ≤ n ≤ 100, 1 ≤ 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.

Sample test(s)
input
4 3
3 1 4 2
output
5
Note
The array given in the sample looks like that: 3, 1, 4, 2, 3, 1, 4, 2, 3, 1, 4, 2. The elements in bold form the largest non-decreasing subsequence.
题意,要求循环数列的最长非递增子序列。
如果,周期是足够大的(大于 2 * n),那么怎样才能最长呢,无疑会出现中间有一段是平的,也就是一个周期内出现次数最多的那个数。因为,一个周期最多有n个不同的数,要求的数列又是递增的,达到n个后,还有更多的,肯定是有重复的数。
就像如图所示
这里写图片描述
所示的n1 n2最长是n段周期。所以,我们可以先把前n * n个数,后n * n 个数算出最长非严格递增子序列。中间那段就是,一个周期出现最多的次数* (T - 2 * n);
时间复杂度为n * n log (n * n);
下面用两种方式求,
第一种,直接用dp推,由于是周期的数列,所以,可以优化到o(n * n * n)的复杂度。
第二种,就直接用二分查找的方法,优化求非递增子序列的方法。
主要思想就是,用一个数组维护一个递增的子序列,如果,新加的无素大于,最后一个无素,直接加入,否则,在数组里,把第一个大于新加的元素的元素更新就可以了。复杂度为o(n * n * log(n * n));

#define N 100005
#define M 100005
#define maxn 205
#define MOD 1000000000000000007
int n,T,dp[N],num[400],b[N],a[N],an;
int Find2(int bn){
    FI(bn) dp[i] = 1;
    int maxx = 0;
    FI(bn){
        int s = min(i,n);
        for(int j = 1;j<=s;j++){
            if(b[i - j] <= b[i]){
                dp[i] = max(dp[i],dp[i - j] + 1);
            }
        }
        maxx = max(maxx,dp[i]);
    }
    return maxx;
}
int Find1(int bn){
    an = 0;
    FI(bn){
        if(an <= 0 || b[i] >= a[an - 1]){
            a[an++] = b[i];
        }
        else {
            int k = (upper_bound(a,a+an,b[i]) - a);
            a[k] = b[i];
        }
    }
    return an;
}
int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
     while(S2(n,T)!=EOF)
    {
        fill(num,0);
        int mn = 0;
        FI(n){
            S(b[i]);
            num[b[i]]++;
            mn = max(mn,num[b[i]]);
        }
        if(T <= 2 * n){
            int bn = n * T;
            FI(bn){
                b[i] = b[i % n];
            }
            printf("%d\n",Find1(bn));
        }
        else {
            int bn = n * 2 * n;
            FI(bn){
                b[i] = b[i % n];
            }
            printf("%d\n",mn * (T - 2 * n) + Find1(bn));
        }
    }
    //fclose(stdin);
    //fclose(stdout);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值