您的位置:首页 > 产品设计 > UI/UE

JeremyGuo膜你赛 t3 azui

2017-10-30 18:59 393 查看
题意:

你有一个长度为 n 的序列,其中第 i 个位置上写了一个浮点数 pipi,表示你如果 在当前位置,那么你有 pi 的概率向右走一步,反之(1−pi)的概率向反方向走一步。 定义一个游戏获胜的概率为: 一个序列,你一开始站在 1 的位置上,按照上述规则进行移动,如果从序列右端 移动出去了,那么就算获胜,如果中途从左端移出,就算失败,问经过了任意次 的移动后获胜的概率。 现在有一长度为 n 的序列,m 个操作,操作分为以下两种: 1、修改一个位置上的 pi 2、询问如果将某一段[l,r]提取出来单独做一个游戏,获胜的概率是多少?

第一行给出两个数 n,m 表示序列的长度和操作的数量 接下来 n 行每行包含两个数 Ai, Bi 这用来表示 pi=Ai/Bi 其中第 i 个表示 pi 接下来 m 行每行第一个数表示操作类型 若为 1,那么接下来三个数分别表示修改的位置和新的 pi=Ai/Bi 若为 2,那么接下来两个证书 L 和 R,表示进行游戏的区间。 

输出的行数应该和操作 2 的数量相同,每行一个整数,最后的答案模 9999991 (质数)输出——就是说分数用整数表示(别告诉我你不知道怎么搞)。 好吧:pi=Ai*Bi^(-1)=Ai*Bi^(MOD-2)然后你把 pi 当成浮点数就行了,不用管别 的

 保证 1≤ n,m ≤ 200000,并且对于每个点的概率 Ai/Bi 保证 Ai≤Bi≤1000

题解

这道题是一道好题,正解是线段树。

可以发现,这道题要求单点修改,区间查询,这些线段树都可以轻松做到,只要你解决了如何维护这颗线段树,这道题也就相当于解决了

维护线段树的难度在于区间合并,对于处理这个问题,有两种思路,暴力列式化简和列方程

这里主要介绍一下来自EnderGZM的列方程做法:

首先对于线段树的每个区间,维护两个值,一个是最左端走出最右端的Plr,另一个是从最右端走出最左端Prl

为了方便,对于合并的另一个相邻区间的这两个值,我们用Qlr和Qrl表示,考虑如何列出方程,见下图



用P[A]表示从A点走出左区间的概率,P,P[C]同理

那么可以得出如下方程:

P[A]=Plr*P[C] 从A走到C,再从C走出

P[C]=Qlr+(1-Qlr)*P[B] 从C走出分两种情况,直接走出与往回走到B再走出

P[B]=(1-Prl)*P[C] 从B走到C,再从C走出

于是就可以通过上述方程解出P[A],这也即是合并后区间的Plr

Prl同理可得

这里直接把结论给出来:

合并后的区间

[b]Plr=plr*qlr/(1-(1-qlr)*(1-prl))


Prl=prl*qrl/(1-(1-qlr)*(1-prl))

于是这道题就可以做了,虽然是数据结构,但是代码也并不难实现,只有不到100行

code:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;
const int MAXN=20000
abbe
5;
const int mod=9999991;
int n,m,t,A,B,P[MAXN];
struct node{
int s,e,plr,prl;
}tree[MAXN<<2];
#define lson i<<1
#define rson i<<1|1
int inv[mod+5];

inline void prepare(int n){
inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
return;
}
inline void Read(int &Ret){
char ch;bool flag=0;
for(;ch=getchar(),!isdigit(ch);)if(ch=='-')flag=1;
for(Ret=ch-'0';ch=getchar(),isdigit(ch);Ret=Ret*10+ch-'0');
flag&&(Ret=-Ret);
}
inline void pushup(int i){
int q = 1ll*(1ll-tree[lson].prl+mod)%mod*(1ll-tree[rson].plr+mod)%mod;
tree[i].plr = 1ll*tree[lson].plr*tree[rson].plr%mod*inv[(1ll-q+mod)%mod]%mod;
tree[i].prl = 1ll*tree[lson].prl*tree[rson].prl%mod*inv[(1ll-q+mod)%mod]%mod;
}
void build(int i,int s,int e){
tree[i].s=s; tree[i].e=e;
if(s==e)
{
Read(A); Read(B);
tree[i].plr=1ll*A*inv[B]%mod;
tree[i].prl=(mod+1ll-tree[i].plr)%mod;
return;
}
int mid=(s+e)>>1;
build(lson,s,mid);
build(rson,mid+1,e);
pushup(i);
}
int pos,cA,cB;
void update(int i){
if(tree[i].s==tree[i].e)
{
tree[i].plr=1ll*cA*inv[cB]%mod;
tree[i].prl=(1-tree[i].plr+mod)%mod;
return;
}
if(pos<=tree[lson].e) update(lson);
else update(rson); pushup(i);
}
int s,e,Plr,Prl;
inline void Merge(int i){
Plr=1ll*Plr*tree[i].plr%mod*inv[((1ll-(1ll-tree[i].plr)*(1ll-Prl)%mod)+mod)%mod]%mod;
Prl=1ll*Prl*tree[i].prl%mod*inv[((1ll-(1ll-tree[i].plr)*(1ll-Prl)%mod)+mod)%mod]%mod;
}
void Query(int i){
if(s<=tree[i].s&&tree[i].e<=e)
{Merge(i); return;}
if(s <= tree[lson].e) Query(lson);
if(e >= tree[rson].s) Query(rson);
}
int main()
{
Read(n); Read(m);
prepare(mod); build(1,1,n);
for(int i=1;i<=m;i++)
{
Read(t);
if(t==1)
{
Read(pos); Read(cA); Read(cB);
update(1);
}
else
{
Read(s); Read(e);
Plr=Prl=1; Query(1);
printf("%d\n",(Plr+mod)%mod);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: