您的位置:首页 > 其它

BZOJ5157 & 洛谷3970:[TJOI2014]上升子序列——题解

2018-06-15 08:48 316 查看

https://www.lydsy.com/JudgeOnline/problem.php?id=5157

https://www.luogu.org/problemnew/show/P3970

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

  1. 是原序列的一个子序列

  2. 长度至少为2

  3. 所有元素都严格递增

如果两个上升子序列相同,那么只需要计算一次。例如:序列{1,2,3,3}有4个上升子序列,分别为{1,2}{1,3},{1,2,3},{2,3}

sb题,但是(可能是因为我脑子不清晰的缘故)就是想不出来,看题解也死活看不懂,写完发现是真sb题……

于是疯狂深呼吸ing不然怕自己连题解都写不出来。

也不知道自己昨天是干了什么结果自己智商突然就飞了。

设f[i]为以i结尾的上升子序列个数,显然f[i]=sigma(f[j]+1)(1<=j<i && a[j]<a[i])

去重也很简单,离散化之后设g[i]为以数i结尾的上升子序列个数,用f更新g,最后累加g并且减去长度为1的序列即可。

然后就能想到把g搬到树状数组上维护,这样g[i]=sigma(g[j]+1)即可。

PS:为了方便统计,直接把g[i]+1扔到树状数组上就行了。

#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+5;
const int p=1e9+7;
inline int read(){
int X=0,w=0;char ch=0;
while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
int n,lim,s
,tr
,f
,pre
,b
;
inline int sum(int x,int y){
x+=y;if(x>=p)x-=p;return x;
}
inline int sub(int x,int y){
x-=y;if(x<0)x+=p;return x;
}
inline int lowbit(int t){return t&(-t);}
inline void add(int x,int y){
for(int i=x;i<=lim;i+=lowbit(i))tr[i]=sum(tr[i],y);
}
inline int ask(int x){
int res=0;
for(int i=x;i;i-=lowbit(i))res=sum(res,tr[i]);
return res;
}
inline void LSH(){
sort(b+1,b+lim+1);
lim=unique(b+1,b+lim+1)-b-1;
for(int i=1;i<=n;i++)
s[i]=lower_bound(b+1,b+lim+1,s[i])-b;
}
int main(){
n=read();
for(int i=1;i<=n;i++)s[i]=b[++lim]=read();
LSH();
for(int i=1;i<=n;i++){
f[i]=ask(s[i]-1)+1;
add(s[i],sub(f[i],f[pre[s[i]]]));
pre[s[i]]=i;
}
printf("%d\n",sub(ask(lim),lim));
return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: