次长上升子序列(LIS 树状数组)

题目描述
最长上升子序列是一道经典的题目,liu_runda很想在模拟赛中考考这个题目,但是他又不想被选手骂出原题,于是就把原题魔改一下再出出来.
对于一个数列a[1],a[2]…a[n], 我们定义子序列是一系列下标的集合: {x1,x2…xm}
其中, 1<=x1<x2<x3…<xm<=n
本题的上升子序列应满足a[x1]<=a[x2]<=a[x3]…<=a[xm], 也就是说, 我们考虑的是非严格的上升子序列(或者说,不下降子序列)
两个子序列不同, 当且仅当有一个下标被一个子序列包含却不被另一个子序列包含.
给出一个数列, 你需要找出所有非严格的上升子序列中第二长的子序列的长度. 它有可能比最长上升子序列短, 也有可能和最长上升子序列一样长.

输入
每个输入包含多组测试数据,第一行一个整数T表示测试数据的组数.
接下来每组数据第一行一个整数n, 表示数列的长度,第二行n个整数表示数列.

输出
T行, 第i行一个数字表示第i组输入的次长上升子序列长度

样例输入
5
10
10 1 8 10 2 6 4 1 5 4
10
8 2 6 1 6 8 10 3 7 4
10
1 8 7 9 9 6 10 3 2 2
10
5 4 8 2 2 2 2 9 3 3
10
8 4 9 7 6 9 10 3 7 3

样例输出
4
4
5
5
4

提示
100%的数据, T=5, 1<=ai<=100000,n≤100000

思路
当组成最大不下降子序列的组成方法不唯一时,次长上升子序列即为最大不下降子序列长度,反之则为最大不下降子序列-1,当数据范围<1e4时,可以用dp直接推出解,本题需要使用树状数组优化,即可得解

代码实现

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=200005;
const int M=1e5;
const int LIM=200;
const int INF=0x3f3f3f3f;
const ll LINF=1e17;
const ull sed=31;
const ll mod=998244353;
const double eps=1e-7;
const double PI=3.14159265358979;
typedef pair<int,int>P;
typedef pair<double,double>Pd;

int n,a[N],dp[N][2],b[N][2],maxb,secb,len;

inline int lowbit(int x) {return x&(-x);}

void addmax(int x,int p)
{
    for(;x<=len;x+=lowbit(x))
    {
        if(b[x][0]<p)
        {
            b[x][1]=b[x][0];
            b[x][0]=p;
        }
        else if(b[x][1]<p) b[x][1]=p;
    }
}

void getmax(int x)
{
    maxb=secb=0;
    for(;x;x-=lowbit(x))
    {
        if(b[x][0]>maxb)
        {
            secb=max(maxb,b[x][1]);
            maxb=b[x][0];
        }
        else secb=max(secb,b[x][0]);
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        len=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            len=max(len,a[i]);
        }
        for(int i=0;i<N;i++)
        {
            b[i][0]=b[i][1]=dp[i][1]=0;
            dp[i][0]=1;
        }
        addmax(a[1],1);
        int maxa=0,seca=0;
        for(int i=2;i<=n;i++)
        {
            getmax(a[i]);
            dp[i][0]=max(dp[i][0],maxb+1);
            if(secb) dp[i][1]=max(dp[i][1],secb+1);
            addmax(a[i],dp[i][0]);
            addmax(a[i],dp[i][1]);
        }
        for(int i=1;i<=n;i++)
        {
            if(dp[i][0]>maxa)
            {
                seca=max(maxa,dp[i][1]);
                maxa=dp[i][0];
            }
            else seca=max(seca,dp[i][0]);
        }
        printf("%d\n",seca);
    }
    return 0;
}

发布了235 篇原创文章 · 获赞 13 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43935894/article/details/105334186