分析:
变成最长上升子序列
第二问一眼dp
但是没有什么用,不会啊STO
第一问nlogn
第二问
dp的思路,设g[i]为以i结尾,长度是f[i]的子序列的所有方案数
我们把f[j]=f[i]-1的所有j(j < i)从小到大依次选出来,
它们的a[j]是单调不升的,
因为f值等于f[i]-1的编号从小到大,
如果存在k < j,a[k ]< a[j],那么f[j]=f[k]+1
j,k在这种情况下是不可能同时转移到i的
这样我们就可以按f[i]值把元素放到不同的集合里
深入考虑集合中的数
例如,我们要转移f[i]=k的点,
显然f[i]=k-1所代表的转移点集合中a[i]单调不升,
而且所有f[i]=k的这些被转移的点中a[i]也是单调不升的
(这个和f[k]=k-1是一个道理啊)
也就是说,假如f[j]=k-1,f[i]=k,j < i,但a[j]>=a[i],
显然j不能作为i的转移点,
那么对于比i大的那些i’(f[i’]=k),
j更不可能是i’的转移点了,
因为a[i’] <= a[i]
这样我们就可以开若干个队列q[]来存储转移点
保证每个队列中的a[i]单调递减,
相应的弹出队首队尾元素
再开个数组记录每个队列中g[i]的和即可
开一个双端队列即可,可以用STL的deque也可以手写
(不过开n个STL的deque巨慢,在cena里直接会T)
tip
很少用stl
所以代码用的deque
(感觉stl确实方便,废话。。。)
队列里的元素编号从小到大,
a值单调减
我们要新开一个数组ans
计算每一个队列中的元素和,
这样在转移的时候就变成O(1)的了
因为对于f[i]=k的转移,
等于f值等于k的a[i]按编号排序的话一定是单调不升的
所以直接修改ans来优化转移
再说一下队首和队尾的出队条件
队首
在进行转移的时候维护出队,因为队列中的a一定是单调不升的
所以一旦ai
这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
const int mod=1000000007;
const int N=300005;
ll a[N],f[N],len,g[N],ans[N],l[N];
deque <int> q[N]; //双端队列
int n;
ll mo(ll a,ll b)
{
if (a+b>=0) return (a+b)%mod;
else return (a+b+mod)%mod;
}
void doit()
{
int i,j,k;
len=1;
l[1]=a[1]; f[1]=1;
for (i=2;i<=n;i++)
{
if (a[i]>l[len])
{
l[++len]=a[i];
f[i]=len;
continue;
}
int r=lower_bound(l+1,l+1+len,a[i])-l;
l[r]=a[i];
f[i]=(ll)r;
}
printf("%lld ",len);
//for (i=1;i<=n;i++) printf("%d ",f[i]);
}
void solve()
{
int i,j;
for (i=1;i<=n;i++)
{
if (f[i]==1) g[i]=1; //dp的初始值
else
{
ll p=f[i]-1; //转移点的f
while (!q[p].empty()&&a[q[p].front()]>=a[i]) //严格上升
{
ans[p]=mo(ans[p],-g[q[p].front()]); //无法转移的状态g值减掉
q[p].pop_front();
}
g[i]=ans[p];
}
ll p=f[i]; //加入队列
while (!q[p].empty()&&a[q[p].back()]==a[i]) //值相同保存后
{
ans[p]=mo(ans[p],-g[q[p].back()]);
q[p].pop_back();
}
q[p].push_back(i);
ans[p]=mo(ans[p],g[i]);
}
ll cnt=0,t=0;
for (i=n;i;i--)
if (f[i]==len&&a[i]>t) //防止值相同的元素g重复统计
cnt=mo(cnt,g[i]),t=a[i];
printf("%lld",cnt);
}
int main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
scanf("%d",&n);
for (int i=n;i>=1;i--) scanf("%lld",&a[i]);
doit();
solve();
return 0;
}