hdu-4991(dp+线段树)

对于线段树有了更新的认识。

题意:从无序串中求长度为k的严格上升序列的个数。

设dp[ i ][ j ]为以位置i的数为结尾的长度为j的严格上升序列的个数。

则dp[ i ][ j ] = sum { dp[ k ][ j - 1 ] | 1 <= k <= i - 1 }

用线段树优化。

将输入序列排序,然后从1~n更新线段树。

sum[ idx ][ len ]位置保存以输入时下标为idx位置为结尾的长度为len的串个数。

#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int mod=123456789;
const int N=10002;
int sum[101][N<<2];//线段树存储占N*4
struct node
{
    int v;
    int idx;
}a[N];
bool operator <(const node &a,const node &b)
{
    return a.v<=b.v;//有=号 保证重复的数不会更新多次
}
void update(int idx,int len,int suml,int root,int l,int r)//更新idx下标为结尾长度为len的串数目为suml
{
    if(l==r) {
        sum[len][root]=suml;
        return;
    }
    int mid=(l+r)>>1;
    if(idx<=mid) update(idx, len, suml, root*2, l, mid);
    else update(idx, len, suml, root*2+1, mid+1, r);

    sum[len][root]=sum[len][root*2]+sum[len][root*2+1];
    sum[len][root]%=mod;
}
int query(int la,int ra,int len,int root,int l,int r)//统计输入的la~ra下标区间长度为len的序列数
{
    if(la>ra) return 0;
    if(la<=l&&ra>=r) return sum[len][root];
    int mid=(l+r)>>1;
    if(ra<=mid) return query(la,ra,len,root*2,l,mid)%mod;
    if(la>mid) return query(la,ra,len,root*2+1,mid+1,r)%mod;
    int ans=(query(la,ra,len,root*2,l,mid)+query(la,ra,len,root*2+1,mid+1,r))%mod;
    return ans;
}
int main()
{
    int n,k,i,j;
    while(~scanf("%d%d",&n,&k))
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i].v);
            a[i].idx=i;
        }
        memset(sum,0,sizeof(sum));
        sort(a+1,a+1+n);//按照a[i].v自小到大排序,保证了递增顺序
        for(i=1;i<=n;i++)
        {
            update(a[i].idx,1,1,1,1,n);
            for(j=1;j<k;j++)
            {
                int suml=query(1,a[i].idx-1,j,1,1,n);
                if(!suml) break;
                update(a[i].idx,j+1,suml,1,1,n);
            }
        }
        printf("%d\n",sum[k][1]);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值