题目大意
给出一个序列a,求最长上升子序列及其方案数。
n<=100000,a[i]<=100000
时间限制 1s
空间限制 256M
解题思路
当做到第i位时,线段树上的第x位表示1~i-1中,结尾为x的子序列能取到的最大答案。
每次在线段树上查询0~a[i]-1的最大答案及其方案数。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100006
#define fr(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const ll ding=123456789;
int i,n,typ,x,mx,s1,ans1,tr[maxn*4];
ll s2,ans2,c[maxn*4];
void modify(int v,int st,int en,int x,int y,ll z)
{
if (st==en)
{
if (y>tr[v]) tr[v]=y,c[v]=z;
else if (y==tr[v]) c[v]=(c[v]+z)%ding;
return;
}
int m=(st+en) >> 1;
if (x<=m) modify(v+v,st,m,x,y,z);
else modify(v+v+1,m+1,en,x,y,z);
if (tr[v+v]>tr[v+v+1]) tr[v]=tr[v+v],c[v]=c[v+v];
else if (tr[v+v]<tr[v+v+1]) tr[v]=tr[v+v+1],c[v]=c[v+v+1];
else if (tr[v+v]==tr[v+v+1]) tr[v]=tr[v+v],c[v]=(c[v+v]+c[v+v+1])%ding;
return;
}
void findd(int v,int st,int en,int l,int r)
{
if (st==l && en==r)
{
if (tr[v]>s1) s1=tr[v],s2=c[v];
else if (tr[v]==s1) s2=(s2+c[v])%ding;
return;
}
int m=(st+en) >> 1;
if (r<=m) findd(v+v,st,m,l,r);
else if (l>m) findd(v+v+1,m+1,en,l,r);
else
{
findd(v+v,st,m,l,m);
findd(v+v+1,m+1,en,m+1,r);
}
return;
}
int main()
{
freopen("hamon.in","r",stdin);
freopen("hamon.out","w",stdout);
scanf("%d%d",&n,&typ);
mx=100000;
fr(i,1,mx*4) tr[i]=-1 << 30,c[i]=0;
modify(1,0,mx,0,0,1);
fr(i,1,n)
{
scanf("%d",&x);
s1=s2=0;
findd(1,0,mx,0,x-1);
modify(1,0,mx,x,s1+1,s2);
if (s1+1==ans1) ans2=(ans2+s2)%ding;
else if (s1+1>ans1)
ans1=s1+1,ans2=s2;
}
printf("%d\n",ans1);
if (typ==1) printf("%lld\n",ans2);
return 0;
}