一本通1742题解

这题就是要求最长上升子序列的长度及其个数

暴力代码:

#include<bits/stdc++.h>
#pragma GCC optimize(3)
using namespace std;
const int N=1e5+100;
const long long mod=123456789;
int n,type;
int R[N],f[N],dp[N];  //f[i]表示长度为i的最长上升子序列的最后一个元素的值 
//f[i] 
long long cnt[N];
inline int find(int l,int r,int x){
    while(l<=r)
    {
        int mid=l+r>>1;
        if(f[mid]<x)
            l=mid+1;
        else
            r=mid-1;
    }
    return l;
} 
int main()
{
    scanf("%d %d",&n,&type);
    for(int i=1;i<=n;i++)
        scanf("%d",&R[i]);
    int len=1;
    f[1]=R[1];
    for(int i=2;i<=n;i++)
    {
        if(R[i]>f[len]){
            f[++len]=R[i];
        }
        else{
            int k=find(1,len,R[i]);
            f[k]=R[i];
        }
    }
    printf("%d\n",len);
    if(!type){
        return 0;
    }else
    {
        for(int i=1;i<=n;i++)
            dp[i]=cnt[i]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<i;j++)
                if(R[j]<R[i])
                    if(dp[j]==dp[i])
                        dp[i]=dp[j]+1,cnt[i]=cnt[j]%mod;
                    else if(dp[j]+1==dp[i])
                        cnt[i]=(cnt[i]+cnt[j])%mod;
        long long ans=0;
        for(int i=1;i<=n;i++)   
            if(len==dp[i])
                ans=(ans+cnt[i])%mod;
        printf("%lld",ans%mod);
    }
    return 0;
}

那么想要优化到 $O(nlogn)$,显然需要数据结构来帮忙。仔细观察状态转移方程,发现这其实是一个二维偏序:$i<j\space and\space a[i]>a[j]$。所以,对于第一维我们排序,第二维用数据结构维护。

我们维护树状数组$f[i]\space and\space t[i]$,因为要保证$a[i]>a[j]$,我们在进行$modify(int\space p,int\space x,int\space y)$操作时,直接将 $a_i$ 作为第一维的 $p$,在 $ask$ 操作时从 $a[i]-1$ 开始就一定保证 $a[j]<a[i]$ ,所以呢,大概的代码是这样:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
const long long mod=123456789;
long long f[N],t[N],a[N],Max,tres,tans;
int n,type;
inline int lowbit(int i){
    return i&(-i); 
}
inline void modify(long long p,long long x,long long y){
    for(;p<=Max;p+=lowbit(p))
        if(f[p]<x)
            f[p]=x,t[p]=y;
        else if(f[p]==x)
            t[p]=(t[p]+y)%mod;
    return ;
}
int main(){
    scanf("%d %d",&n,&type);
    for(int i=1;i<=n;i++)   
        scanf("%lld",&a[i]),Max=max(Max,a[i]);
    modify(a[1],1,1);
    for(int i=2;i<=n;i++){
        long long ans=0,res=1;
        for(int j=a[i]-1;j>=1;j-=lowbit(j))
            if(f[j]>ans)
                ans=f[j],res=t[j];
            else if(f[j]==ans)
                res=(res+t[j])%mod;
            modify(a[i],ans+1,res);
    }
    for(int j=Max;j>=1;j-=lowbit(j))
        if(f[j]>tans)
            tans=f[j],tres=t[j];
        else if(f[j]==tans)
            tres=(tres+t[j])%mod;
    printf("%lld",tans);
    if(type)
        printf("\n%lld",tres);
    return 0;
}

技巧应用:对于二位偏序,一维排序,二维数据结构;对于三维偏序,一维排序,二维数据结构,三维cdq分治

posted @ 2021-10-05 21:49  青D  阅读(36)  评论(0编辑  收藏  举报