码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-10-17 14:39:59      阅读:184      评论:0      收藏:0      [点我收藏+]

标签:c++   表示   相同   子序列   lin   递增   log   题解   树状   

题面

数组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);
}

 

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

标签:c++   表示   相同   子序列   lin   递增   log   题解   树状   

原文地址:https://www.cnblogs.com/NSD-email0820/p/9803435.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!