Description
给出序列 a a a, q q q次询问,每次询问 1 − ∏ i = l r ( 1 − a i x ) 1-\prod\limits_{i=l}^r (1-\frac{a_i}{x}) 1−i=l∏r(1−xai),绝对误差不超过 1 0 − 6 10^{-6} 10−6。
Solution
∏
i
=
l
r
(
1
−
a
i
x
)
=
e
∑
i
=
l
r
l
n
(
1
−
a
i
x
)
\prod_{i=l}^r (1-\frac{a_i}{x})=e^{\sum\limits_{i=l}^r ln(1-\frac{a_i}{x})}
i=l∏r(1−xai)=ei=l∑rln(1−xai)
∑
i
=
l
r
l
n
(
1
−
a
i
x
)
=
∑
i
=
l
r
−
∑
j
=
1
∞
a
i
j
j
⋅
x
j
\sum_{i=l}^r ln(1-\frac{a_i}{x})=\sum_{i=l}^r-\sum_{j=1}^{\infty}\frac{a_i^j}{j\cdot x^j}
i=l∑rln(1−xai)=i=l∑r−j=1∑∞j⋅xjaij
这个式子在
a
i
x
\frac{a_i}{x}
xai较小时
j
j
j只要取到
20
20
20就够精度了,较大时怎么办?
不慌,
a
i
x
\frac{a_i}{x}
xai较大时
1
−
a
i
x
1-\frac{a_i}{x}
1−xai必然较小,所以取出来单独算即可。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
typedef double db;
const int N=6e5+10,M=20;
const db eps=1e-10,lim=0.5;
int a[N];
int read(){
char ch=' ';int t=0;
for(;ch<'0' || ch>'9';ch=getchar());
for(;ch>='0' && ch<='9';ch=getchar()) t=(t<<1)+(t<<3)+ch-48;
return t;
}
db s[N][M+5];
int f[20][N];
int lg[N],n;
int cmax(int x,int y){
return a[x]>a[y]?x:y;
}
void pre(){
fo(i,2,n) lg[i]=lg[i>>1]+1;
fo(i,1,lg[n])
fo(j,1,n-(1<<i)+1)
f[i][j]=cmax(f[i-1][j],f[i-1][j+(1<<(i-1))]);
}
int qmax(int l,int r){
int t=lg[r-l+1];
return cmax(f[t][l],f[t][r-(1<<t)+1]);
}
db ans,tmp;
db now;
int X;
int mx=0;
db calc(int l,int r){
db t=0,z=1;
fo(i,1,M) z*=now,t+=(s[r][i]-s[l-1][i])*z;
return t;
}
void solve(int l,int r){
if(l>r || ans<eps) return;
int p=qmax(l,r);
db w=a[p]*1.0/X;
if(w<=lim) return;
tmp-=calc(p,p),ans*=1-w;
solve(l,p-1),solve(p+1,r);
}
int main()
{
n=read();
int q=read();
fo(i,1,n){
a[i]=read(),mx=max(mx,a[i]);
f[0][i]=i;
}
pre();
fo(i,1,n){
db x=a[i]*1.0/mx,t=1;
fo(j,1,M) t*=x,s[i][j]=s[i-1][j]-t/j;
}
for(;q--;){
int l=read(),r=read(),x=read();X=x;
now=1.0/x*mx;
ans=1,tmp=calc(l,r);
solve(l,r);
ans*=exp(tmp);
printf("%.7lf\n",1-ans);
}
}