我觉得我的数学需要回炉重造。。
题目大意:
给一个n个元素构成序列,从中选取子序列使得选取的子序列能够满足i<j,a[i]^j<a[j]<i。问一共有多少种子序列选择方案。n<=100,a[i]<=100
思路:
其实我一开始的就想当然的认为了如果i<j<k,若a[i]^j<a[j]^i,那么就会有a[i]^k<a[k]^i。
这样的话就可以用dp解决。
令dp[i]表示以i为起点往右的满足条件的子序列方案数。
那么对于i来说,我们可以枚举i右边的j,若满足a[i]^j<a[j]^i,那么我们有
f[i]=∑f[j]
关键是a[i]^j和a[j]^i的判断,如果直接算的话肯定会炸的。
然后我就卡在这里了。。。
参考了一下别人的思路,可以用除法,把两个做一次除法,然后转化成(a[i]/a[j])^i * x^(j-i)。
这样做其实对精度要求还蛮高的,不过居然过了。
更好的做法其实是对两边取对数。
log(a[i]^j)=j * log(a[i])
log(a[j]^i)=i * log(a[j])
这个可以预处理,然后直接dp就好了。
最后提一下,一开始的结论是正确的。看了推导以后就不证了
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
typedef long long ll;
const ll mod=1000000007;
bool c[102][102];
ll f[102];
double a[102];
double calc(double x,int y){
double ans=1;
while(y){
if(y%2)ans=ans*x;
x=x*x;
y=y/2;
}
return ans;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],f[i]=1;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
double x,y;
x=a[i];y=a[j];
if(x>=y)continue;
double tmp=calc(x/y,i);
for(int k=1;k<=j-i;k++){
tmp*=x;
}
if(tmp<1)c[i][j]=true;
}
}
for(int i=n-1;i>=1;i--){
for(int j=i+1;j<=n;j++){
if(c[i][j])f[i]+=f[j],f[i]%=mod;
}
}
ll ans=0;
for(int i=1;i<=n;i++)ans+=f[i],ans%=mod;
cout<<ans<<endl;
return 0;
}
京公网安备 11010502036488号