【51NOD1376】最长递增子序列的数量

题面

数组A包含N个整数(可能包含相同的值)。设S为A的子序列且S中的元素是递增的,则S为A的递增子序列。如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS)。A的LIS可能有很多个。例如A为:{1 3 2 0 4},1 3 4,1 2 4均为A的LIS。给出数组A,求A的LIS有多少个。由于数量很大,输出Mod 1000000007的结果即可。相同的数字在不同的位置,算作不同的,例如 {1 1 2} 答案为2。
1 <= N <= 50000,0 <= Ai <= 10^9

分析

记得以前在光华好像有一道题就是算这个,当时就只想着在dp里动手脚,但是正确性不能保证。现在也没啥新想法TAT
一搜题解,啥,CDQ啊??奇怪的姿势。最后找到了一篇姿势优美的题解,但是思路只有一行。又只有从代码推思路。
回想n2的dp的做法,f[i]表示以a[i]为结尾的最长上升子序列的长度,我们应该应该找一个在最大的f[j]中找尽量小的j,使j<i并且a[j]<a[i],即可使f[i]=f[j]+1.
就是找j的这一步,用树状数组优化为logn。

用树状数组同时维护len和num,表示长度和数量。每次询问a[i]之前树状数组上的的len和num来更新f[i]。只要每一次长度被更新,数量也同步更新。
注意到a[i]太大了,应该离散化一下才能用树状数组做。

代码

#include<bits/stdc++.h>
using namespace std;
#define N 50050
#define mod 1000000007
int n,cnt,a[N],tmp[N];
struct email
{
    int len,num;
}ans,f[N],t[N];
inline int lowbit(int x){return x&(-x);}

inline void dp(email &x,email y)
{
    if(x.len>y.len)return;
    if(x.len<y.len)x=y;
    else x.num=(x.num+y.num)%mod;
}

inline email query(int x)
{
    email ret;ret.len=0,ret.num=1;
    for(;x;x-=lowbit(x))
        dp(ret,t[x]);
    return ret;
}

inline email add(int x,email y)
{
    for(;x<=cnt;x+=lowbit(x))
        dp(t[x],y);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),tmp[i]=a[i];
    sort(tmp+1,tmp+1+n);
    cnt=unique(tmp+1,tmp+1+n)-(tmp+1);
    for(int i=1;i<=n;i++)a[i]=lower_bound(tmp+1,tmp+1+cnt,a[i])-tmp;
    for(int i=1;i<=n;i++)f[i]=query(a[i]-1),f[i].len++,add(a[i],f[i]);
    for(int i=1;i<=n;i++)dp(ans,f[i]);
    printf("%d\n",ans.num);
}

 

posted @ 2018-10-17 12:36  WJEMail  阅读(190)  评论(0编辑  收藏  举报