update:新方法,树状数组优化dp,复杂度

一道很简单的dp+数论题。。。

我们设dp[i]表示以i结尾的子序列中成立的方案数

那么,就有转移:

答案就很显然了:

于是这题就基本做完了,不过,麻烦的是,你需要做这个式子

如果我们直接算,最大的情况就可能搞出个,然后爆炸(当然,你要打个高精也没有任何问题)

但是,我们如何快速的进行判断呢?

法一:

判断的大小(a>b)

首先明显的,如果有x>=y那么,此时一定是

否则,我们考虑一般的判断方法——作差法和作除法

很明显,作差法不好做,我们考虑使用作除法,那么有:


如果
K>1则
K=1则一样大
K<1则

注意到,因为我们之前已经判断了x>=y的情况,现在我们只需要判断x<y的情况,所以此时
我们可以直接用double维护这个式子,先把前面的求出来,多次乘x,若中途大于1了,就可以停止了,因为x>=1,这样乘完a-b次后一定会大于1。则说明K>1
反之,如果乘完a-b次后,值任然小于1,则说明K<1

如此判断即可复杂度

当然,这么判断肯定还是麻烦了,那么,我们考虑简单的判断,想起以前我的学长和数学老师告诉我的一个结论:
不等号两边同时取对数后,若底数大于1,不等号依旧成立(前提是可以取对)
所以,我们来看现在的:

法二:

,我们同时取对后(随便拿个大于1的数为底都行),就变成了:


化简下就变成了:

而这个两个数的值,很明显,都很小,可以直接double处理,我们就做完了

复杂度

不过,需要注意的是,不管是double还是long double都是有一定的精度范围的,所以,最好卡下精度(虽然这题并不需要)
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=101,mod=1e9+7;
const double eps=1e-5;
int a[N];
int dp[N];//以i结尾的方案数 
inline bool recheck(double x,double y){//x是否小于y
    return x<y&&fabs(x-y)>eps;
} 
inline bool check(int x,int y){//a[x]^y<a[y]^x是否成立 
    return recheck(y*log(a[x]),x*log(a[y]));
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
    }
    int ans=0;
    for(int i=1;i<=n;++i){//序列以i结尾 
        dp[i]=1;//只选i
        for(int j=1;j<i;++j){
            dp[i]=(dp[i]+check(j,i)*dp[j])%mod;
        }
        ans=(ans+dp[i])%mod;
    }
    printf("%d",ans);
    return 0;
}

之前,我们推出式子如果的话,那么j就可以放在i的后面,不过,如果我们再化简下,将它变成: 的话
就是一个只与i/j相关的式子了,我们设它为b[i]的话,那么,我们只需要找到i前面比b[i]小的数就可以放在它后面了。
所以,我们考虑将b[i]离散化,这样,我们就可以使用一个数据结构去维护它,就可以将复杂度优化至了!

#include<bits/stdc++.h>
using namespace std;
const int N=101,mod=1e9+7;
double a[N],b[N];int c[N],s[N],dp[N],n,m;
inline void insert(int x,int y){
    while(x<=m){
        s[x]=(s[x]+y)%mod;x+=(x&-x);
    }
}
inline int find(int x){
    int res=0;
    while(x){
        res=(res+s[x])%mod;x-=(x&-x);
    }
    return res;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%lf",&a[i]);
        a[i]=(log(a[i])/i);
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    m=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;++i){
        c[i]=lower_bound(b+1,b+m+1,a[i])-b;
    }
    insert(c[1],1);int ans=1;
    for(int i=2;i<=n;++i){
        dp[i]=1+find(c[i]-1);
        insert(c[i],dp[i]);
        ans=(ans+dp[i])%mod;
    }
    printf("%d",ans);
    return 0;
}