【纪中集训2019.3.26】获取名额

描述

描述:

\(n\)场省选选拔赛,每场的前\(a_i\)名可以获得省选资格;

假设\(Moorhsum\)只参加第\([l,r]\)场比赛,且每一场排名在\([1,x]\)随机;

求他获得省选资格的概率;

范围:

$1 \le n , q \le 600000 \ , \ 1 \le x \le 10^9 \ , \ 1 \le l \le r \le n $;

数据保证对于任意\(i\)\(a_i \lt x\);

当你的答案和标准答案的误差不超过\(10^{-6}\)是被认为是正确的;

\(512MB\);

题解

  • 只需要统计$ \Pi_{i=l}^{r} (1-\frac{a_i}{x})$;

  • $= exp(\sum_{i=l}^{r} \ ln(1-\frac{a_i}{x})) $;

  • \(\frac{a_i}{x} \lt 1\)所以里面可以直接泰勒展开:

  • $ = - \sum_{i=l}^{r} \sum_{j=1}^{\infty} \frac{a_i^j}{j \ x^j} \ = \ -\sum_{i=j}{\infty}\frac{1}{ixi} \sum_{j=l}^{r} a_j^i $;

  • 貌似只要\(\infty\)\(50\)左右,预处理$\sum_{i=l}^{r} a_j^i $就可以了;

  • 实测一下会发现范围和精度有很大问题;

  • 范围问题直接考虑将\(x=/max(a_i),a_i/=max(a_i)\)就不会爆\(double\)了;

  • \(a_i/x\)较大时,\(ln\)里面的东西会比较接近\(0\),很难保证精度;

  • \(a_i/x\)设置一个\(lim\),当\(a_i/x>lim\)时直接算,再递归左右区间;

  • \(lim\)\(0.5\)是可以过的;

    #include<bits/stdc++.h>
    #define ld double
    #define il inline
    using namespace std;
    const int N=600010;
    int n,m,lg[N],f[N][21],bin[21];
    ld sum[N][51],a[N],X,tmp;
    il char gc(){
    	static char*p1,*p2,s[1000000];
    	if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
    	return(p1==p2)?EOF:*p1++;
    }
    il int rd(){
    	int x=0;char c=gc();
    	while(c<'0'||c>'9')c=gc();
    	while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc();
    	return x;
    }
    il int Max(int x,int y){return a[x]>a[y]?x:y;}
    il int ask(int x,int y){
    	int t=lg[y-x+1];
    	return Max(f[x][t],f[y-bin[t]+1][t]);
    }
    il ld cal(int l,int r){
    	ld re=0,t=1;
    	for(int i=1;i<=50;++i){
    		t*=X;re-=(sum[r][i]-sum[l-1][i])/(t*i);
    	}
    	return exp(re);
    }
    void solve(int l,int r){
    	int mid=ask(l,r);
    	if(a[mid]/X<0.5){tmp*=cal(l,r);return;}
    	tmp*=1-a[mid]/X;
    	if(l<mid)solve(l,mid-1);
    	if(mid<r)solve(mid+1,r);
    } 
    int main(){
    	freopen("orz.in","r",stdin);
    	freopen("orz.out","w",stdout);
    	n=rd();m=rd();ld mx=0;
    	for(int i=1;i<=n;++i)a[i]=rd(),mx=max(a[i],mx);
    	for(int i=1;i<=n;++i)a[i]/=mx;
    	for(int i=bin[0]=1;i<=20;++i)bin[i]=bin[i-1]<<1;
    	lg[0]=-1;for(int i=1;i<=n;++i)f[i][0]=i,lg[i]=lg[i>>1]+1;
    	for(int i=1;i<=20;++i)
    	for(int j=1;j+bin[i]-1<=n;++j){
    		f[j][i]=Max(f[j][i-1],f[j+bin[i-1]][i-1]);
    	}
    	for(int i=1;i<=n;++i){
    		ld t=1;
    		for(int j=1;j<=50;++j){
    			t*=a[i];
    			sum[i][j]=sum[i-1][j]+t;
    		}
    	}
    	for(int i=1,l,r;i<=m;++i){
    		l=rd();r=rd();X=rd()/mx;
    		tmp=1;solve(l,r);
    		ld ans=1-tmp;
    		printf("%.10lf\n",ans); 
    	}
    	return 0;
    }
    
posted @ 2019-03-26 18:52  大米饼  阅读(227)  评论(0编辑  收藏  举报