CF597C Subsequences 题解

题意

略。

分析

一眼望去发现是 dp。容易想到状态 \(dp_{i,j}\) 表示以第 \(i\) 位为结尾长度为 \(j\) 的严格上升子序列个数,转移方程为:

\[dp_{i,j}=\sum\limits_{a_k<a_i \and k<i} dp_{k,j-1} \]

但是这样做的时间复杂度为 \(\mathcal{O}(n^2m)\),显然会 TLE,因此考虑优化:

可以想到把 dp 值放到树状数组中,这样转移时就不用再枚举一重 \(k\) 了,可以开 \(n\) 个树状数组,\(tr_i\) 存严格上升子序列长度为 \(i\) 的 dp 值。这样转移时就可以把时间复杂度降为 \(\mathcal{O}(\log_2n)\),总时间复杂度 \(\mathcal{O}(nm\log_2n)\)

代码

#define int long long
const int MAXN=1e5+7;
int T,n,m,a[MAXN],b[MAXN],t,tr[17][MAXN];
inline int lowbit(int x){return x&(-x);}
void update(int x,int k,int p){for(;x<=n;x+=lowbit(x)) tr[p][x]=(tr[p][x]+k);}
int query(int x,int p)
{
	int res=0;
	for(;x;x-=lowbit(x)) res+=tr[p][x];
	return res;
}
signed main()
{
    int i,j;
    qread(n,m);m++;
    for(i=1;i<=n;i++) qread(a[i]);update(1,1,0);
    for(i=1;i<=n;i++)
    {
        if(a[i]==1) 
        {
            update(1,1,1);
            continue;
        }
        for(j=1;j<=m;j++)
        {
            int tmp=query(a[i]-1,j-1);
            update(a[i],tmp,j);
        }
    }
    printf("%lld\n",query(n,m));
    return 0;
}
posted @ 2022-06-18 12:25  l_x_y  阅读(58)  评论(0编辑  收藏  举报