hdu2227 Find the nondecreasing subsequences(dp+线段树or树状数组优化)

Problem Description
How many nondecreasing subsequences can you find in the sequence S = {s1, s2, s3, …., sn} ? For example, we assume that S = {1, 2, 3}, and you can find seven nondecreasing subsequences, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}.

Input
The input consists of multiple test cases. Each case begins with a line containing a positive integer n that is the length of the sequence S, the next line contains n integers {s1, s2, s3, …., sn}, 1 <= n <= 100000, 0 <= si <= 2^31.

Output
For each test case, output one line containing the number of nondecreasing subsequences you can find from the sequence S, the answer should % 1000000007.

Sample Input
3
1 2 3

Sample Output
7

大致题意:告诉你n个数,让你求出这个数列不递减的子序列的个数,对1e9+7取模

思路:很容易可以想到dp方程,假设dp[i]表示到第i个位置的所有不递减子序列个数.
状态转移方程即:dp[i]=sum(dp[j])+1(j < i &&num[j]<=num[i]),但这样子的话时间复杂度就达到了n^2.考虑到有个sum,所以我们可以先将这n个数离散化,然后再用线段树(或者树状数组)来优化,那么查询sum的时间就降到了logn。

代码如下

代码1.

/*
线段树
*/
#include<cstring> 
#include<cstdio> 
#include<iostream>   
#include <algorithm>  
#define ll long long int  
#define lson l,m,rt<<1  
#define rson m+1,r,rt<<1|1  
using namespace std;
const int M=1e5+5; 
const int mod=1e9+7;
int dp[M<<2];  

struct node
{
    int value;
    int ii;
}num[M];

int cmp(node a,node b)
{
    return a.value<b.value;
}

inline void PushPlus(int rt)  
{   
    dp[rt]= (dp[rt<<1]+dp[rt<<1|1])%mod; 
}  
void Updata(int p,ll add, int l, int r, int rt)//单点更新,p位置上的数值增加add 
{  

    if( l == r )  
    {   
        dp[rt]=(dp[rt]+add)%mod;
        return ;  
    }  

    int m = ( l + r ) >> 1;  
    if(p <= m)  
        Updata(p, add, lson);  
    else  
        Updata(p, add, rson);  

    PushPlus(rt);  
}  

ll Query(int L,int R,int l,int r,int rt)  
{  
    if(L>R)
    return 0;
    if( L <= l && r <= R )  
    {  
        return dp[rt];  
    }  
    int m = ( l + r ) >> 1;  

    ll ans=0;  
    if(L<=m )  
        ans=(ans+Query(L,R,lson))%mod;  
    if(R>m)  
        ans=(ans+Query(L,R,rson))%mod;  
    return ans;  
}  

int main()  
{    
    int mark[M];
    int n,k; 
    while(scanf("%d",&n)!=EOF)
    {
        memset(dp,0,sizeof(dp));

        for(int i=1;i<=n;i++)
        {
            scanf("%d",&num[i].value);
            num[i].ii=i;
        }
        sort(num+1,num+1+n,cmp);//离散化 
        int k=-1,t=0;
        for(int i=1;i<=n;i++)
        {
            if(num[i].value!=k)
            {
                ++t;
                k=num[i].value;
            }
            mark[num[i].ii]=t;
        }

        for(int i=1;i<=n;i++)
        {
            int sum=(Query(1,mark[i],1,t,1)+1)%mod;//查询sum(dp[j]),j<=mark[i] 
            Updata(mark[i],sum,1,t,1);//修改mark[i]位置上的值 
        }
        printf("%d\n",Query(1,t,1,t,1));
    }
    return 0;  
}  

代码2.

#include<cstring> 
#include<cstdio> 
#include<iostream>   
#include <algorithm>  
#define ll long long int   
using namespace std;
const int N=1e5+5; 
const int mod=1e9+7;
ll dp[N];
int n;

struct node
{
    int value;
    int ii;
}num[N];

int cmp(node a,node b)
{
    return a.value<b.value;
}
int lowbit(int x)
{
    return x&-x;
}

int sum(int x)
{
    int s=0;
    while(x>0)
    {
        s=(s+dp[x])%mod;
        x=x-lowbit(x);
    }
    return s;
}

void add(int x,int date)
{
    while(x<=n)
    {
        dp[x]=(dp[x]+date)%mod;
        x=x+lowbit(x);
    }
}

int main()  
{
    int mark[N];
    while(scanf("%d",&n)!=EOF)
    {
        memset(dp,0,sizeof(dp));

        for(int i=1;i<=n;i++)
        {
            scanf("%d",&num[i].value);
            num[i].ii=i;
        }
        sort(num+1,num+1+n,cmp);//离散化 
        int k=-1,t=0;
        for(int i=1;i<=n;i++)
        {
            if(num[i].value!=k)
            {
                ++t;
                k=num[i].value;
            }
            mark[num[i].ii]=t;
        }

        for(int i=1;i<=n;i++)
        {
            int ans=(sum(mark[i])+1)%mod;
            add(mark[i],ans);
        }
        printf("%d\n",sum(t));
    }
    return 0;  
}   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值