Codechef Chef at the Food Fair
阿新 • • 發佈:2019-01-07
題目描述
大廚住的城市裡辦了一場美食節。一條街上開設了\(N\)個攤位,編號為\(1 ∼ N\)。這天開始時,
第\(i\)個攤位的食物會導致食物中毒的概率是\(P_i\)。
在這一天中,大廚發現某些攤位可能會根據顧客的反饋提供沒那麼有毒的食物。你需要處理
\(Q\)個詢問,詢問有以下兩類:
• 0 L R:求出:如果要吃遍\([L, R]\)內所有攤位的食物,那麼不會食物中毒的概率是多少;
• 1 L R T:\[[L, R]\]中的所有攤位的食物會導致食物中毒的概率變為了原來的\(T\)倍。\(T\)是一
個小於 1 的非負實數。
資料範圍
• \(1 ≤ N, Q ≤ 10^5\)
• \(0 ≤ Pi ≤ 0.9\)
• \(1 ≤ L ≤ R ≤ N\)
解題思路
發現這個東西特別不好維護......
\[ ans=\prod_{i=L}^{R}(1-p_i)=e^{ln\prod_{i=L}^{R}(1-p_i)}=e^{\sum_{i=L}^{R}ln(1-p_i)} \]
上面就是正解的轉化,因為\(ln\)這個函式特別好泰勒展開
什麼是泰勒展開,就是一個複雜的函式我們用一個多項式擬合
思考的過程就是對於一個點\(x_0\),這個點\(g(x_0)=f(x_0),g'(x_0)=f'(x_0),g''(x_0)=f''(x_0)…\)於是推出
\[ f(x)=\sum_{i=0}^{n}\frac{f^{(i)}(x_0)}{i!}(x-x_0)i \]
回到這題,我們對\(ln(1-x)\)泰勒展開,為了簡便我們使\(x_0=0\),於是不難轉化成
\[ ln(1-x)=-\sum_{i=1}^{\infty}\frac{x^i}{i} \]
所以直接線段樹維護即可.
#include<cmath> #include<cstdio> #define ls (x<<1) #define rs ((x<<1)+1) using namespace std; const int maxn=100005,W=100; double a[maxn<<2][W],tag[maxn<<2],p[maxn]; int n,m; void up(int x){for (int i=1;i<W;i++) a[x][i]=a[ls][i]+a[rs][i];} void add(int x,double T){double w=T;for (int i=1;i<W;i++) a[x][i]*=w,w*=T;} void Build(int x,int L,int R){ tag[x]=1;if (L==R){double w=p[L];for (int i=1;i<W;i++) a[x][i]=w/i,w*=p[L];return;} int mid=L+(R-L>>1);Build(ls,L,mid);Build(rs,mid+1,R);up(x); } void pushdown(int x){add(ls,tag[x]);add(rs,tag[x]);tag[ls]*=tag[x];tag[rs]*=tag[x];tag[x]=1;} void change(int x,int l,int r,int L,int R,double T){ if (l<r) pushdown(x); if (l==L&&r==R){add(x,T);tag[x]*=T;return;} int mid=l+(r-l>>1); if (R<=mid) change(ls,l,mid,L,R,T);else if (L>mid) change(rs,mid+1,r,L,R,T);else change(ls,l,mid,L,mid,T),change(rs,mid+1,r,mid+1,R,T);up(x); } double ask(int x,int l,int r,int L,int R){ if (l<r) pushdown(x); if (l==L&&r==R){double w=0;for (int i=1;i<W;i++) w+=a[x][i];return w;} int mid=l+(r-l>>1); if (R<=mid) return ask(ls,l,mid,L,R);else if (L>mid) return ask(rs,mid+1,r,L,R);else return ask(ls,l,mid,L,mid)+ask(rs,mid+1,r,mid+1,R); } int main(){ freopen("exam.in","r",stdin); freopen("exam.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%lf",&p[i]); Build(1,1,n); while(m--){ int h,L,R;double T;scanf("%d%d%d",&h,&L,&R); if (!h) printf("%.8lf\n",exp(-ask(1,1,n,L,R)));else scanf("%lf",&T),change(1,1,n,L,R,T); } return 0; }