题意很明确,让求序列中上升长度等于k的子序列个数。
这里采用线段树进行求解,最多维护50棵线段树,每一棵线段树记录长度为i的序列个数。
输入数据后进行离散化,这里注意不能使用map,会超时,只能说spoj的服务器太渣渣了。离散化之后就可以对线段树读入数据,每一个数据读入后,依次维护以当前节点结尾长度从1到k的序列长度的个数,这个值记录在对应线段树的对应节点上。以v为结尾长度为k的序列数+=以v-1为节点长度为k-1的序列个数,依次类推便能得到最终结果。
题目的核心在于,记录的是每个节点序列长度为k的序列数,这样记录能避免同一个序列多次计算,每一个长度确定尾节点确定的序列都可以由之前的递推出来,那么序列个数也可以由之前的递推得到。至于为什么是v-1,其实是线段树从1到v-1的和,左节点已经确定,故可省略。而一个长度为k-1的序列中的两个节点不会因为同一个序列而计算两次,这是本题的关键所在。
时间卡的紧,尽量优化。。。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <cstdio>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define delf int m=(l+r)>>1
int sum[51][40001];
int n,m,s;
const int mod=5000000;
void build(int k,int l,int r,int rt)
{
sum[k][rt]=0;
if (l==r)
return ;
delf;
build(k,lson);
build(k,rson);
return ;
}
void pushup(int k,int rt)
{
sum[k][rt]=(sum[k][rt<<1]+sum[k][rt<<1|1])%mod;
}
int query(int k,int v,int l,int r,int rt)
{
if (v<1)
return 0;
if (r<=v)
return sum[k][rt]%mod;
delf;
if (m>=v)
return query(k,v,lson)%mod;
else
return (query(k,v,lson)+query(k,v,rson))%mod;
}
void update(int k,int c,int v,int l,int r,int rt)
{
if (l==r)
{
sum[k][rt]+=c;
return ;
}
delf;
if (m>=v)
update(k,c,v,lson);
else
update(k,c,v,rson);
pushup(k,rt);
return ;
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
int a[10001],b[10001];
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
s=unique(b+1,b+n+1)-(b+1);
/*
map <int,int> mymap; //不能用map进行离散化,会超时。
for (int i=1;i<=s;i++)
mymap[b[i]]=i;
*/
for (int i=1;i<=m;i++)
build(i,1,s,1);
for (int i=1;i<=n;i++)
{
int j=lower_bound(b+1,b+s,a[i])-b; //离散化,求位置
//cout<<j<<endl;
update(1,1,j,1,s,1); //第一次更新,直接对该位置序列长度为1的加1
for (int t=2;t<=m;t++)
{
//cout<<mymap[a[i]]<<endl;
int c=query(t-1,j-1,1,s,1)%mod; //获得序列长度为t-1,值在1~j-1内的序列个数
update(t,c,j,1,s,1); //更新
//cout<<"ANS: "<<sum[m][1]<<endl;
}
//cout<<endl;
}
printf("%d\n",sum[m][1]);
}
}