YbtOJ 严格上升子序列数

12 篇文章 0 订阅

算法分析:

首先很容易想到 dp (没做过最长上升子序列模板的可以去看看)。

f[i][j]表示以i结尾,长度为j的严格上升子序列的个数。

则:

f[i][j] = 1 (j=1)

f[i][j] = {sum_{k=1,a[i]>a[k]}^{i-1} dp[k][j-1]} (jeq 1)

但一看时间复杂度O(Tn^{2}m)肯定超时了。

算法优化:

这时想到了可以用树状数组来维护方案数。

我们将数组离散化,再建m个树状数组,分别维护长度从1到m的方案数,用来优化 dp,复杂度O(Tnmlogn) 显然就能过了。

具体实现看代码:

#include<bits/stdc++.h>
#define int long long //一定注意开long long 
using namespace std;
inline int read(){ //快读没什么好说的 
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48) ; ch=getchar();}
    return x*f;
}
const int M = 1010;
const int mod = 1e9+7; //别忘了取模 
int n,m;
int sum[M][M],f[M][M]; //sum是维护树状数组  f是用来dp 
int T,cnt,ans;
struct dat{
    int val,num;
    friend bool operator < (dat a,dat b){ //重载运算符,当然在外面写函数也行 
        if(a.val == b.val) return a.num > b.num;
        else return a.val < b.val; //按值从小到大排序 
    }
}a[M];
inline int lowbit(int x) {return x & (-x);} //树状数组经典操作 
void update(int x,int y,int w){ //把x这个位置加上w ,y表示这是第几个树状数组 
    while(x <= n){
        sum[x][y] = (sum[x][y] + w)%mod;
        x += lowbit(x);
    }
}
int query(int x,int y){ //求x这个位置的值 , y表示这是第几个树状数组 
    int ans=0;
    while(x){
        ans = (ans + sum[x][y])%mod; //一定随时取模 
        x -= lowbit(x);
    }
    return ans%mod;
}
signed main(){
    T=read();
    while(T--){
        cnt++;
        ans = 0;
        memset(sum,0,sizeof(sum)); //别忘初始化
        n=read(),m=read();
        for(register int i(1) ; i<=n ; i=-~i) a[i].val = read(),a[i].num = i; //离散化操作 
        sort(a+1,a+n+1); //排序 
        for(register int i(1) ; i<=n ; i=-~i){
            for(register int j(1) ; j<=m ; j=-~j){
                if(j == 1) f[i][j] = 1;
                else f[i][j] = query(a[i].num-1,j-1); //开始dp 
                update(a[i].num,j,f[i][j]); //然后将值加到树状数组中
            }
        }
        for(register int i(m) ; i<=n ; i=-~i) ans = (ans+f[i][m])%mod; //最后是从每个点开始的方案数之和 
        printf("Case #%d: %lld
",cnt,ans%mod);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值