两天来唯一一道可做题……其他的都是什么神仙题啊【拍桌】
因为要维护
∏ i = l r ( 1 − p i ) \prod_{i=l}^{r} (1-p_i) i=l∏r(1−pi)
这个东西,然后还有一个区间乘,直接搞就算是用线段树维护也肯定会 T \text{T} T,考虑用别的方法维护。
此时会想到一个常用技巧,把 ∏ \prod ∏ 转换成 ∑ \sum ∑ 来做,也就是借助 ln \ln ln 函数来把乘变成加,再用线段树就很好维护了。
所以我们可以得到:
∏ i = l r ( 1 − p i ) = e ∑ i = l r ln ( 1 − p i ) \prod_{i=l}^{r}(1-p_i)=e^{\sum_{i=l}^{r}\ln(1-p_i)} i=l∏r(1−pi)=e∑i=lrln(1−pi)
但是 ln ( 1 − p i ) \ln(1-p_i) ln(1−pi) 这个东西也不好维护,考虑 Taylor \text{Taylor} Taylor 展开,可以得到:
ln ( 1 − x ) = − ∑ i = 1 ∞ x i i \ln(1-x)=-\sum_{i=1}^{\infty}\frac{x^i}{i} ln(1−x)=−i=1∑∞ixi
维护个 100 100 100 项就行了。
#include
#define MAXN 100005
#define S 100
using namespace std;
int n,m;
double a[MAXN],t[MAXN<<2][S+5],tag[MAXN<<2];
void PushUp(int rt)
{
for(int i=1;i<=S;i++) t[rt][i]=t[rt<<1][i]+t[rt<<1|1][i];
}
void Add(int rt,double val)
{
tag[rt]*=val;
double cnt=val;
for(int i=1;i<=S;i++)
{
t[rt][i]*=cnt;
cnt*=val;
}
}
void PushDown(int rt)
{
if(tag[rt]!=1)
{
Add(rt<<1,tag[rt]);
Add(rt<<1|1,tag[rt]);
tag[rt]=1;
}
}
void BuildSegmentTree(int rt,int l,int r)
{
tag[rt]=1;
if(l==r)
{
double cnt=a[l];
for(int i=1;i<=S;i++)
{
t[rt][i]=cnt;
cnt*=a[l];
}
return;
}
int mid=l+r>>1;
BuildSegmentTree(rt<<1,l,mid);
BuildSegmentTree(rt<<1|1,mid+1,r);
PushUp(rt);
}
void Modify(int rt,int l,int r,int tl,int tr,double T)
{
if(tl<=l && r<=tr)
{
Add(rt,T);
return;
}
PushDown(rt);
int mid=l+r>>1;
if(tl<=mid) Modify(rt<<1,l,mid,tl,tr,T);
if(tr>mid) Modify(rt<<1|1,mid+1,r,tl,tr,T);
PushUp(rt);
}
double Query(int rt,int l,int r,int tl,int tr)
{
double res=0;
if(tl<=l && r<=tr)
{
for(int i=1;i<=S;i++) res+=t[rt][i]/i;
return res;
}
PushDown(rt);
int mid=l+r>>1;
if(tl<=mid) res+=Query(rt<<1,l,mid,tl,tr);
if(tr>mid) res+=Query(rt<<1|1,mid+1,r,tl,tr);
return res;
}
int main()
{
freopen("food.in","r",stdin);
freopen("food.out","w",stdout);
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lf",&a[i]);
BuildSegmentTree(1,1,n);
while(m--)
{
int opt,x,y;
double z;
scanf("%d %d %d",&opt,&x,&y);
if(!opt) printf("%.8lf\n",exp(-Query(1,1,n,x,y)));
else
{
scanf("%lf",&z);
Modify(1,1,n,x,y,z);
}
}
return 0;
}