bzoj5157 [Tjoi2014]上升子序列

http://www.elijahqi.win/archives/3108
题目描述

给定一个只包含整数的序列(序列元素的绝对值大小不超过10^9),你需要计算上升子序列的个数,满足如下条件的称之为一个上升子序列:

是原序列的一个子序列
长度至少为2
所有元素都严格递增
如果两个上升子序列相同,那么只需要计算一次。例如:序列{1,2,3,3}有4个上升子序列,分别为{1,2}{1,3},{1,2,3},{2,3}

输入输出格式

输入格式:

输入的第一行是一个整数n,表示序列长度。接下来一行是n个整数,表示序列。

输出格式:

输出仅包含一行,即原序列有多少个上升子序列。由于答案可能非常大,你只需要输出答案模10^9+7的余数。

输入输出样例

输入样例#1: 复制

4
1 2 3 3
输出样例#1: 复制

4
说明

数据范围
对于 30% 的数据,N ≤ 5000

对于 100% 的数据,N ≤ 10^5

设dp[i]表示以a[i]为结尾的上升子序列个数的增量 因为有重复的 (都是+1的)

所以用权值树状数组维护以a[i]的这些dp值 然后每次做的时候要把上几次的减去 (去重)且这个上几次的需要累加

然后最后答案因为都多算了自己所以减去即可

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=1e5+10;
const int mod=1e9+7;
inline void add(int &x,int v){x=x+v>=mod?x+v-mod:x+v;}
inline void dec(int &x,int v){x=x-v<0?x-v+mod:x-v;}
int n,a[N],a1[N],nn,s[N],last[N],dp[N],ans;
inline void add1(int x,int v){while(x<=nn) add(s[x],v),x+=x&-x;}
inline int query(int x){int tmp=0;while(x) add(tmp,s[x]),x-=x&-x;return tmp;}
int main(){
    freopen("bzoj5157.in","r",stdin);
    n=read();for (int i=1;i<=n;++i) a[i]=a1[i]=read();
    sort(a1+1,a1+n+1);nn=unique(a1+1,a1+n+1)-a1-1;
    for (int i=1;i<=n;++i){
        a[i]=lower_bound(a1+1,a1+nn+1,a[i])-a1;
        add(dp[i],query(a[i]-1)+1);
        dec(dp[i],last[a[i]]);
        add(ans,dp[i]);add1(a[i],dp[i]);last[a[i]]+=dp[i];
    }dec(ans,nn);printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值