[关闭]
@xiaoziyao 2021-08-06T22:36:55.000000Z 字数 1631 阅读 1019

loj#3401. 「2020-2021 集训队作业」Old Problem解题报告

解题报告


#3401. 「2020-2021 集训队作业」Old Problem解题报告:

dengls的题,/se。

题意

给定 个正整数 次询问,每次给定 ,求

精度差在 以内即可。(

分析

精度题。

我么发现这个东西很难维护,于是考虑采取奇技淫巧,分治解决这一问题:

若当前递归到的区间为 ,那么我们查看这一区间的最大值是否不小于 ,若大于,则暴力计算并递归至最大值对应的左右区间,否则就玄妙计算一下,并返回。

如果我们不考虑后面,我们发现 约为 ,已经满足精度要求,于是此处的复杂度正确。

对于后面的计算,我们可以利用的性质仅有区间最大值小于

由于乘法太大,不好处理,套路地取 ,我们要处理的便是:

此时我们只要处理了 ,就可以直接前缀和一波带走了。

考虑直接套用 的公式在 处进行泰勒展开

由于区间所有数小于 ,所以 小于 ,而恰好在 处展若干项 时精度就非常高了。

时间复杂度

代码

  1. #include<stdio.h>
  2. #include<math.h>
  3. const int maxn=600005,maxk=21;
  4. int n,q,X;
  5. int lg[maxn],a[maxn],maxx[maxn][maxk];
  6. double ans;
  7. double sum[maxn][maxk];
  8. int calc(int l,int r){
  9. return a[l]>a[r]? l:r;
  10. }
  11. int query(int l,int r){
  12. int k=lg[r-l+1];
  13. return calc(maxx[l][k],maxx[r-(1<<k)+1][k]);
  14. }
  15. void solve(int l,int r){
  16. if(l>r||ans<1e-6)
  17. return ;
  18. int p=query(l,r);
  19. if(l==r||a[p]*2>=X){
  20. ans*=(1-1.0*a[p]/X);
  21. solve(l,p-1),solve(p+1,r);
  22. return ;
  23. }
  24. double res=0,mul=X;
  25. for(int i=1;i<=20;i++,mul*=X)
  26. res+=(sum[r][i]-sum[l-1][i])/mul;
  27. ans*=exp(-res);
  28. }
  29. int main(){
  30. scanf("%d%d",&n,&q);
  31. lg[0]=-1;
  32. for(int i=1;i<=n;i++){
  33. scanf("%d",&a[i]),maxx[i][0]=i,lg[i]=lg[i>>1]+1;
  34. double mul=a[i];
  35. for(int j=1;j<=20;j++,mul*=a[i])
  36. sum[i][j]=sum[i-1][j]+mul/j;
  37. }
  38. for(int i=1;i<=20;i++)
  39. for(int j=1;j+(1<<i)-1<=n;j++)
  40. maxx[j][i]=calc(maxx[j][i-1],maxx[j+(1<<(i-1))][i-1]);
  41. for(int i=1;i<=q;i++){
  42. int l,r;
  43. scanf("%d%d%d",&l,&r,&X),ans=1;
  44. solve(l,r);
  45. printf("%.10lf\n",1-ans);
  46. }
  47. return 0;
  48. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注