【线段树 泰勒展开】Codechef April Challenge 2018 Chef at the Food Fair

第一次写泰勒展开;本地和CC差距好大

题目大意

大厨住的城市里办了一场美食节。一条街上开设了$N$个摊位,编号为$1∼N$。这天开始时,第$i$个摊位的食物会导致食物中毒的概率是$P_i$。在这一天中,大厨发现某些摊位可能会根据顾客的反馈提供没那么有毒的食物。你需要处理$Q$个询问,询问有以下两类:

0 L R:求出:如果要吃遍$[L,R]$内所有摊位的食物,那么不会食物中毒的概率是多少;
1 L R T:$[L,R]$中的所有摊位的食物会导致食物中毒的概率变为了原来的$T$倍。$T$是一个小于$1$的非负实数。

对于前 $20\%$ 的数据,$n,m\le 2000$

另有 $20\%$ 的数据,$T\le 0.5$

对于 $100\%$ 的数据,满足 $n,m\le 10^5,0\le T<1,P_i\le 1,1\le L\le R\le n$,保证输入数据不超过 $6$ 位小数。


题目分析

注意到维护的操作有些不同寻常。

  • 第一:维护的是$\prod (1-P_i)$
  • 第二:每次操作是区间乘法

对于要求支持区间乘的问题,有一种转化套路是将它取$\ln$,那么问题就变成了维护区间和。

那么这题中还需要处理$\ln (1-P_i)$,将它泰勒展开得到$\ln(1-x)=x-{1\over 2}x^2-{1\over 3}x^3-...-{1\over n}x^n+R(x)$。我们一如既往地爆精度,只需要保留这个式子的前$MAXD=100$项和。维护时则是开$MAXD$颗线段树对每类次项分别处理区间乘法。

需要注意的是,这题需要一些常数技巧。我最先是开了$f[MAXD]$颗封装好的线段树,但由于数组的第一维是更频繁访问的一维,所以实际运行效率会比较低。如果采用形如$f[maxn<<2][MAXD]$的做法,就会快非常多。

非常迷的一点是,同一份代码在本地考试的数据下,极限数据要跑个4~5s;但是交到CC上就rank2了……

 1 #include<bits/stdc++.h>
 2 const int maxn = 100035;
 3 const double eps = 1e-12;
 4 
 5 int n,m,MAXD;
 6 double p[maxn],w[maxn],K,S;
 7 double f[maxn<<2][103],tag[maxn<<2];
 8 
 9 int read()
10 {
11     char ch = getchar();
12     int num = 0, fl = 1;
13     for (; !isdigit(ch); ch=getchar())
14         if (ch=='-') fl = -1;
15     for (; isdigit(ch); ch=getchar())
16         num = (num<<1)+(num<<3)+ch-48;
17     return num*fl;
18 }
19 void pushup(int rt)
20 {
21     for (int i=1; i<=MAXD; i++)
22         f[rt][i] = f[rt<<1][i]+f[rt<<1|1][i];
23 }
24 void pushdown(int rt)
25 {
26     double v = tag[rt], s = tag[rt];
27     if (fabs(1.0-v) > eps){
28         tag[rt<<1] *= v, tag[rt<<1|1] *= v;
29         for (int i=1; i<=MAXD; i++)
30             f[rt<<1][i] *= s, f[rt<<1|1][i] *= s, s *= v;
31         tag[rt] = 1.0;
32     }
33 }
34 void build(int rt, int l, int r)
35 {
36     tag[rt] = 1.0;
37     if (l==r){
38         for (int i=1; i<=MAXD; i++)
39             w[l] *= p[l], f[rt][i] = w[l]/i;
40         return;
41     }
42     int mid = (l+r)>>1;
43     build(rt<<1, l, mid);
44     build(rt<<1|1, mid+1, r);
45     pushup(rt);
46 }
47 double query(int rt, int L, int R, int l, int r)
48 {
49     if (L <= l&&r <= R){
50         double ret = 0;
51         for (int i=1; i<=MAXD; i++)
52             ret += f[rt][i];
53         return ret;
54     }
55     int mid = (l+r)>>1;
56     double ret = 0;
57     pushdown(rt);
58     if (L <= mid) ret += query(rt<<1, L, R, l, mid);
59     if (R > mid) ret += query(rt<<1|1, L, R, mid+1, r);
60     return ret;
61 }
62 void modify(int rt, int L, int R, int l, int r, double c)
63 {
64     if (L <= l&&r <= R){
65         double s = c;
66         tag[rt] *= s;
67         for (int i=1; i<=MAXD; i++)
68             f[rt][i] *= s, s *= c;
69         return;
70     }
71     int mid = (l+r)>>1;
72     pushdown(rt);
73     if (L <= mid) modify(rt<<1, L, R, l, mid, c);
74     if (R > mid) modify(rt<<1|1, L, R, mid+1, r, c);
75     pushup(rt);
76 }
77 int main()
78 {
79     n = read(), m = read();
80     for (int i=1; i<=n; i++) scanf("%lf",&p[i]), w[i] = 1.0;
81     MAXD = 100;
82     build(1, 1, n);
83     for (int i=1; i<=m; i++)
84     {
85         int opt = read(), l = read(), r = read();
86         if (opt){
87             scanf("%lf",&K);
88             modify(1, l, r, 1, n, K);
89         }else{
90             K = query(1, l, r, 1, n);
91             printf("%.8lf\n",exp(-K));
92         }
93     }
94     return 0;
95 }

END

猜你喜欢

转载自www.cnblogs.com/antiquality/p/10385631.html