LOJ#6048. 「雅礼集训 2017 Day10」数列(线段树)

题面

传送门

题解

我的做法似乎非常复杂啊……

首先最长上升子序列长度就等于把它反过来再接到前面求一遍,比方说把\(2134\)变成\(43122134\),实际上变化之后的求一个最长上升子序列和方案数就是答案了

最长上升子序列随便求求,主要是这个方案数很麻烦啊……我的做法是对每一个长度开一个动态开点线段树,然后每次在对应的长度里二分跑前缀和

其实这里完全不用动态开点线段树的,直接把权值离散一下然后一棵线段树就够了,跑得飞快

其实这里连线段树都不需要直接树状数组就可以维护前缀最大值和方案之和了

然后没有然后了

//minamoto
#include<bits/stdc++.h>
#define R register
#define inline __inline__ __attribute__((always_inline))
#define fp(i,a,b) for(int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(int i=(a),I=(b)-1;i>I;--i)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=4e5+5,P=1e9+7,M=(N<<5)+5;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
int sum[M],lc[M],rc[M],rt[N],tot;
int a[N],f[N],g[N],b[N],m,n,res,mx,lim;
void query(int p,int l,int r,int x){
    if(!p||l==r)return res=add(res,sum[p]),void();
    int mid=(l+r)>>1;
    if(x<=mid)query(lc[p],l,mid,x);
    else res=add(res,sum[lc[p]]),query(rc[p],mid+1,r,x);
}
void ins(int &p,int l,int r,int x,int val){
    if(!p)p=++tot;sum[p]=add(sum[p],val);
    if(l==r)return;
    int mid=(l+r)>>1;
    x<=mid?ins(lc[p],l,mid,x,val):ins(rc[p],mid+1,r,x,val);
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read();
    fp(i,1,n)a[i]=b[i]=read();
    sort(b+1,b+1+n),lim=unique(b+1,b+1+n)-b-1;
    fp(i,1,n)a[i]=lower_bound(b+1,b+1+lim,a[i])-b;
    reverse(a+1,a+1+n);
    fp(i,1,n)a[(n<<1)-i+1]=a[i];
    n=(n<<1),m=0,b[0]=0;
    fp(i,1,n){
        if(a[i]>b[m])f[i]=++m,b[m]=a[i];
        else{
            int k=lower_bound(b+1,b+1+m,a[i])-b;
            f[i]=k,b[k]=a[i];
        }
        if(f[i]==1)g[i]=1;
        else res=0,query(rt[f[i]-1],1,lim,a[i]-1),g[i]=res;
        if(i>(n>>1)&&f[i]==f[n-i+1])g[i]=dec(g[i],g[n-i+1]);
        ins(rt[f[i]],1,lim,a[i],g[i]);
        cmax(mx,f[i]);
    }
    res=0;
    fp(i,(n>>1)+1,n)if(f[i]==mx){
        res=add(res,g[i]);
        if(f[n-i+1]==mx)res=add(res,g[n-i+1]);
    }
    res=mul(res,ksm(2,(n>>1)-mx));
    printf("%d %d\n",mx,res);
    return 0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10712142.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值