JZOJ 4920 降雷皇(最长上升子序列、线段树)

来源:互联网 发布:ipad pro 生产力 知乎 编辑:程序博客网 时间:2024/06/05 15:14

题目大意

给出一个序列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;}
1 0
原创粉丝点击