51NOD 1376 最长递增子序列的数量 [CDQ分治]

1376 最长递增子序列的数量


首先可以用线段树优化$DP$做,转移时取$0...a[i]$的最大$f$值

但我要练习$CDQ$

$LIS$是二维偏序问题,偏序关系是$i<j,\ a_i<a_j$

$CDQ$分治可以解决偏序问题

$CDQ(l,r)\ :$

$CDQ(l,mid)$

$[l,r]$按$a$排序,$[l,mid] \rightarrow\ [mid+1,r]$

$CDQ(mid+1,r)$

 

这个排序没法用归并排序,因为你要用最优的$f[k],k\in [mid+1,r]$来更新$k$的右面,必须先$[l,mid] \rightarrow\ [mid+1,r]$获得最优的$f[k]$才行,而那些计数类问题就不需要了

我尝试了很多写法,最后分治里还是采用了间接排序,这样不影响$i<j$这个关系

 

[2017-02-25]不排序用一个维护区间最大值的数据结构也可以,更新的时候取$0...a[i]$的最大$f$值(这样你还分治什么啊?!)

 

注意严格递增

 

于是$LIS$现在可以用$CDQ$水过啦!!!

其实二维的最长上升子序列用$CDQ$分治是没有意义的,无论如何都比数据结构维护多一个$log$

该死一下午就写这玩意了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=5e4+5,MOD=1e9+7;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int n,a[N],ref[N];
inline bool cmp(int x,int y){return a[x]==a[y]?x>y:a[x]<a[y];}//strict
inline void mod(int &x){if(x>=MOD) x-=MOD;}
int f[N],g[N];
void CDQ(int l,int r){
    if(l==r) return;
    int mid=(l+r)>>1;
    CDQ(l,mid);
    for(int i=l;i<=r;i++) ref[i]=i;
    sort(ref+l,ref+r+1,cmp);
    int mx=0,cnt=0;
    for(int i=l;i<=r;i++){
        int id=ref[i];
        if(id<=mid){
            if(f[id]>mx) mx=f[id],cnt=g[id];
            else if(f[id]==mx) mod(cnt+=g[id]);
        }else{
            if(mx+1>f[id]) f[id]=mx+1,g[id]=cnt;
            else if(f[id]==mx+1) mod(g[id]+=cnt);
        }
    }
    CDQ(mid+1,r);
}
int main(){
    freopen("in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++) a[i]=read(),f[i]=g[i]=1;
    CDQ(1,n);
    int mx=0,cnt=0;
    for(int i=1;i<=n;i++){
        if(f[i]>mx) mx=f[i],cnt=g[i];
        else if(f[i]==mx) mod(cnt+=g[i]);
    }
    printf("%d",cnt%MOD);
}

 

posted @ 2017-02-24 16:44  Candy?  阅读(710)  评论(0编辑  收藏  举报