HDOJ5542 The Battle of Chibi(LIS拓展问题,树状数组优化转移方程)

传送门
题意很简单:给定一个长度为N的序列A,求A有多少个长度为M的严格递增子序列。

先考虑暴力的dp.设dp[i][j]表示的是以i结尾长度为j的LIS的数量.转移方程:
dp[i][j] = sum{dp[k][j-1] 0 <= k < j && a[i] > a[k]}.
代码:

fir(i,1,n){
    fir(j,1,min(i,m)){
        fir(k,0,j-1){
            if(a[i] > a[k]){
                dp[i][j] += dp[k][j-1];
            }
        }
    }
}

这个算法是n^3的,题目数据是1e3,过不去.考虑优化.
观察转移方程和最内层的循环可以发现的是,待选集合是随着i增大而增大的,这启发我们可以用某种数据结构去优化内层循环.小于a[i]的个数的和这个属性可以用树状数组或者线段树去储存,为了方便我们选择树状数组.
具体做法就是开一个二维的树状数组.c[i][j]第一维表示的是长度.第二维维护权值序列.因为有负数的原因,我们可以选择离散化.
复杂度(O(nmlogn))
代码:

#pragma GCC optimize(2)
#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=(int)b;++i)
#define afir(i,a,b) for(int i=(int)a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define mpr(a,b) make_pair(a,b)
#include <bits/stdc++.h>

using namespace std;
const int N = 1e3+10;
const int mod = 1e9+7;

inline void read(int &a){
    int x = 0,f=1;char ch = getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    a = x*f;
}
int tt,t,n,m,a[N];
vi all;
LL dp[N][N],c[N][N];
LL ask(int len,int p){
    LL res = 0;
    for(;p;p-=p&-p) res = (res+c[len][p])%mod;
    return res;
}
void add(int len,int p,int v){
    for(;p<=tt;p+=p&-p) c[len][p] = (c[len][p] + v)%mod;
}

int getpos(int x){
    return lower_bound(ALL(all),x) - all.begin();
}
int main(){
    read(t);
    fir(tot,1,t){
        printf("Case #%d: ",tot);
        read(n);read(m);
        all.clear();
        all.pb(-2e9);
        all.pb(-2e9+1);
        fir(i,1,n)
            read(a[i]),all.pb(a[i]);
        mem(dp,0);
        mem(c,0);
        dp[0][0] = 1;
        sort(ALL(all));
        all.erase(unique(ALL(all)),all.end());
        tt = all.size();
        fir(i,1,n) a[i] = getpos(a[i]);
        add(0,getpos(-2e9+1),1);
        fir(i,1,n){
            fir(j,1,min(i,m)){
                dp[i][j] = (dp[i][j]+ask(j-1,a[i]-1))%mod;
                add(j,a[i],dp[i][j]);
            }
        }
        LL ans = 0;
        fir(i,m,n) ans = (ans+dp[i][m])%mod;
        printf("%lld\n",ans);
    }   
    
    return 0;
}    


/*
    dp[i][j] 表示以i结尾,长度为j的LIS有多少个.
    dp[i][j] = sum{dp[k][j-1] 0 <= k < j && a[i] > a[k]}.

*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值