传送门
解析:
首先我们还是从最基础的 O ( n 2 ) O(n^2) O(n2)的DP讲起
O ( n 2 ) O(n^2) O(n2)
显然我们只需要处理出前 i i i个位置中,以每个位置结尾的最长上升子序列长度以及以这个位置结尾的最长上升子序列的个数,然后就可以做到 O ( n ) O(n) O(n)枚举所有 i i i之前的位置进行状态更新就行了。
然而我们发现能够对当前状态进行更新的状态 j j j只有 a [ j ] a[j] a[j]比当前的 a [ i ] a[i] a[i]小的状态。
于是有了这种 O ( n log n ) O(n\log n) O(nlogn)的做法。
O ( n log n ) O(n\log n) O(nlogn)
离散化之后权值最多只有 n n n种。
所以我们用一个长度为 n n n的树状数组维护一下状态转移就行了。就是从前缀比 a [ i ] a[i] a[i]小的转移,然后更新所有后缀比 a [ i ] a[i] a[i]大的树状数组节点。
为了方便的进行状态转移,可以写一个结构体,然后重载运算符。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
cs int N=50004;
cs int mod=1000000007;
int uni,n;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a>=b?a-b:a-b+mod;}
struct node{
int len,cnt;
void operator+=(cs node &b){
if(len>b.len)return;
if(len<b.len)*this=b;
else cnt=add(cnt,b.cnt);
}
}t[N],f[N];
#define lowbit(x) (x&(-x))
inline void update(int pos,cs node &a){
for(;pos<=uni;pos+=lowbit(pos))t[pos]+=a;
}
inline node query(int pos){
node res=(node){0,1};
for(;pos;pos-=lowbit(pos))res+=t[pos];
return res;
}
int a[N],b[N];
signed main(){
n=getint();
for(int re i=1;i<=n;++i)a[i]=b[i]=getint();
sort(b+1,b+n+1);
uni=unique(b+1,b+n+1)-b-1;
for(int re i=1;i<=n;++i)a[i]=lower_bound(b+1,b+uni+1,a[i])-b;
for(int re i=1;i<=n;++i){
f[i]=query(a[i]-1);++f[i].len;
update(a[i],f[i]);
}
node ans=(node){0,0};
for(int re i=1;i<=n;++i)ans+=f[i];
cout<<ans.cnt<<"\n";
return 0;
}