作为一名osu!玩家,这道题成功吸引到了我。。。
题意
长度为n的序列,给出每一个数字可能为1的概率
ai
,每个数字为0的概率为
1−ai
。两个操作:修改某个数字的概率,询问一段区间得分期望,得分计算方式如下。
将玩家完成一张地图的01串中所有的0删去,则这个串可能会断裂成若干段连续的1。对于一段长度为
L
的1011101110
,则删除所有0后得到的是“1”“111”和“111”
。因此这个玩家的得分为(1+1+1)+(9+3+1)+(9+3+1)=29
。
题解
这是出题人的题解
下面简述此题解内容。
分值的计算为
∑mi=1(L2i+Li+1)
,
m
为极大连续1子段的段数,
将其分为三部分分别计算期望:
∑L2i,∑Li
和
∑1
。将这三部分分别记为
S2,S1
和
S0
。记01串第
i
位为
S1
的期望
复杂度 O(n)
S0
的期望
S0
的实际意义是01串中极大1字段的个数。关注每个这样子串的起始位置,这样的位置和子串是一一对应的,只需要统计有多少位置是子串的起始位置。
考虑第
i
位,它是起始位置当且仅当第
复杂度 O(n)
E(S2)
的计算
由于出现了平方,计算
S2
的期望并没有计算
S0
和
S1
那么简单。
定义两个数列
{leni}
与
{expi}
,
leni
表示
{bi}
构成的01串最长的全是1的后缀的长度,
expi
表示
{bi}
构成的01串
S2
的期望值。
len0=0,exp0=0
。
对于
leni
,如果
bi=1
,则
leni=leni−1+1
,否则
leni=0
,故
对于 expi ,如果 bi=1 , Δ=(leni−1+1)2−len2i−1=2leni−1+1 ,否则 Δ=0 ,故
复杂度 O(n)
用线段树维护信息与查询
维护
S1
S1=L.S1+R.S1
维护
S0
若
L
L.S0=a+b(1−a)+c(1−b)
R.S0=d+e(1+d)+f(1−e)
S0=a+b(1−a)+c(1−b)+d(1−c)+e(1−d)+f(1−e)=L.S0+R.S0−cd
故记录区间左右端点的
ai
值就可以维护
S0
了。
维护
S2
leni=aileni−1+ai
expi=expi−1+2aileni−1+ai
这是一个线性变换,表示矩阵为
(leni−1,expi−1,1)⋅T=(leni,expi,1)
所以拿 (0,0,1) 去乘一整个区间的矩阵就能得到 (lenk,expk,1) , k 为区间长度,
这道题没有区间修改不用打标记,每个区间直接记录矩阵,由于矩阵乘法的结合律,可以用线段树维护区间的矩阵乘积。但是直接这么写常数太大。
观察 T :
可以看到一个矩阵我们只需记录四个值即可,大大减小了常数。
复杂度 O(n+mlogn)
这道题还有个简化版本,在BZOJ4318,给出PO姐的题解
代码
/// by ztx
/// blog.csdn.net/hzoi_ztx
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v) for(i=v.begin();i!=v.end();i++)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
if (CH == '-') NEG = true , CH = getchar() ;
while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
if (NEG) ret = -ret ;
}
#define kN 500010LL
#define kT 2000010LL
#define M ((L+R)/2)
#define l(o) (o<<1)
#define r(o) (o<<1|1)
#define left l(o),L,M
#define right r(o),M+1,R
struct mat { lf x[4]; };
inline mat Mul(const mat&a,const mat&b) {
return (mat){a.x[0]*b.x[0],a.x[0]*b.x[1]+a.x[1],a.x[2]*b.x[0]+b.x[2],a.x[2]*b.x[1]+a.x[3]+b.x[3]};
}
inline void One(mat&a) {
a.x[0] = 1.0, a.x[1] = a.x[2] = a.x[3] = 0;
}
inline lf Ans(const mat&a) {
return a.x[3];
// (0,0,1) * (a b 0) = (c,d,1)
// (0 1 0) ^
// (c d 1)
}
int n, ql, qr;
lf a[kN], qw, qa0, qa1, qra;
mat qa2;
lf s0[kT], s1[kT], la[kT], ra[kT];
mat t[kT];
void update(int o) {
la[o] = la[l(o)], ra[o] = ra[r(o)];
s0[o] = s0[l(o)]+s0[r(o)]-ra[l(o)]*la[r(o)];
s1[o] = s1[l(o)]+s1[r(o)];
t[o] = Mul(t[l(o)],t[r(o)]);
}
void Build(int o=1, int L=1, int R=n) {
if (L == R) {
t[o].x[0] = t[o].x[2] = t[o].x[3] = a[L], t[o].x[1] = a[L]*2;
la[o] = ra[o] = s0[o] = s1[o] = a[L];
return ;
}
Build(left), Build(right);
update(o);
}
void Modify(int o=1, int L=1, int R=n) {
if (L == R) {
a[L] = qw;
t[o].x[0] = t[o].x[2] = t[o].x[3] = qw, t[o].x[1] = qw*2;
la[o] = ra[o] = s0[o] = s1[o] = qw;
return ;
}
if (ql <= M) Modify(left);
else Modify(right);
update(o);
}
void Query(int o=1, int L=1, int R=n) {
if (ql<=L && R<=qr) {
qa0 += s0[o]-qra*la[o], qra = ra[o];
qa1 += s1[o];
qa2 = Mul(qa2,t[o]);
return ;
}
if (ql <= M) Query(left);
if (qr > M) Query(right);
}
#undef r
#define r(x) read(x)
int main() {
int m, i, ope;
r(n), r(m);
Rep (i,1,n) scanf("%lf", &a[i]);
Build();
while (m --> 0) {
r(ope);
if (ope) r(ql), scanf("%lf", &qw), Modify();
else r(ql), r(qr), qa0=qa1=qra=0, One(qa2), Query(), printf("%.2f\n", qa0+qa1+Ans(qa2));
}
END: getchar(), getchar();
return 0;
}