[JZOJ] 5837.Omeed

本文探讨了一种结合概率和线段树的数据结构算法,用于解决动态更新区间内元素及计算其期望值的问题。通过定义特定函数f(i),并利用线段树维护区间信息,实现了对区间内每个元素的概率P_i及其对应函数f(i)的高效更新和查询。算法适用于动态维护大量数据的场景,如游戏评分系统等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先摆出来这个式子
\[ score=A\sum S_i+B\sum S_i\times f(i) \]
先研究\(f\)函数(也就是Combo函数)

显然的有
\[ f(i)=P_i(f(i-1)+1)+t(1-P_i)f(i-1) \]
化简得
\[ f(i)=(P_i+t-tP_i)f(i-1)+P_i \]
再考虑一下期望意义下的式子
\[ score=A\sum P_i+B\sum P_i\times f(i) \]
然而,这是不对的,第一个样例都过不了

问题出在哪?就在\(P_i\times f(i)\)这里

\(P_i\)意味着第i位必为1,此时的\(f(i)\)应当在(2)式中将\(P_i\)代为1,也就是此时
\[ score=A\sum P+B\sum P_i\times(f(i-1)+1) \]
到这里,第一部分就是一个区间和,第二部分考场上直接写暴力了

现在考虑一下如何快速维护第二个信息


\[ cost(i,j)=\sum\limits_{k=i}^jP_k\times (f(k-1)+1)=\sum\limits_{k=i}^jP_kf(k-1)+P_k \]

和式里面是一个一次函数的形式,自变量是\(f(x)\),根据(3)式,\(f(x)\)也是一个一次函数的形式

这就可以用线段树维护了,单点修改很好实现,考虑\(P_i+d\)\(d\)的贡献即可

对于一个区间\([L,R]\),记录\(k_1,b_1,k_2,b_2\)表示\(f(R)=k_1f(L-1)+b_1,cost(L,R)=k_2f(L-1)+b_2\)

由于它们都是一次函数,所以一定可以这样表出

现在考虑如何合并区间信息,考虑边界情况,对于一个区间\([i,i]\),根据(3)和(6)有

\(k_1=P_i+t-tP_i ,\ b1=P_i,\ k_2=P_i,\ b_2=P_i\)

考虑一个区间\([L,R]\),中点为\(m\)
\[ \begin{align*} f(m)&=k_1f(L-1)+b_1\\ f(R)&=k_1'f(m)+b_1'\\ \therefore f(R)&=k_1k_1'f(L-1)+k_1'b_1+b_2\\ cost(L,m)&=k_2f(L-1)+b_2\\ cost(m+1,R)&=k_2'f(m)+b_2'\\ \therefore cost(L,R)&=(k_2+k_2'k_1)f(L-1)+b_2+b_2'+k_2'b_1 \end{align*} \]
这样就能合并区间了,由于\(f(L-1)=0\),所以答案区间\([L,R]\)的答案就是
\[ score[L,R]=A\sum_{i=L}^RP_i+B\times b_2 \]
只维护\(f\)是不能做的,考场上并没有想到再维护一个\(cost\),参考了杨神犇的博客
线段树真是处理区间可合并信息的利器

#include<algorithm>
#include<iostream>
#include<cstdio>

using namespace std;

inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
  int ret=0,f=1;char c;
  while(c=gc(),!isdigit(c))f=c=='-'?-1:1;
  while(isdigit(c))ret=ret*10+c-'0',c=gc();
  return ret*f;
}
#define space() putchar(' ')
#define nextline() putchar('\n')
void pot(int x){if(!x)return;pot(x/10);putchar('0'+x%10);}
void out(int x){if(!x)putchar('0');if(x<0)putchar('-'),x=-x;pot(x);}

const int MAXN = 500005;
const int MOD = 998244353;

int mul(int x,int y){return (1ll*x*y)%MOD;}
void Mul(int &x,int y){x=mul(x,y);}
int sub(int x,int y){return x-y<0?x-y+MOD:x-y;}
int add(int x,int y){if(y>=0)return x+y>MOD?x+y-MOD:x+y;else return sub(x,-y);}
void Add(int &x,int y){x=add(x,y);}

inline int qpow(int x,int y){
  int ret=1;
  while(y){
    if(y&1)Mul(ret,x);
    Mul(x,x);
    y>>=1;
  }
  return ret;
}
inline int inv(int x){return qpow(x,MOD-2);}

int n,q,t,dt,ta,tb,A,B;
int p[MAXN];

#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
struct Node{
  int k1,k2,b1,b2;
  Node(int x=1,int y=0,int u=1,int v=0){k1=x;b1=y;k2=u;b2=v;}
}node[MAXN<<2];
inline Node merge(const Node &x,const Node &y){
  return Node(
    mul(x.k1,y.k1),
    add(mul(x.b1,y.k1),y.b1),
    add(x.k2,mul(y.k2,x.k1)),
    add(mul(y.k2,x.b1),add(x.b2,y.b2))
  );
}
void build(int L,int R,int cur,int l,int r){
  if(l==r){
    node[cur]=Node(sub(add(p[l],t),mul(t,p[l])),p[l],p[l],p[l]);
    return;
  }
  if(L<=mid)build(L,R,ls,l,mid);
  if(mid <R)build(L,R,rs,mid+1,r);
  node[cur]=merge(node[ls],node[rs]);
}
void update(int pos,int cur,int l,int r,int w){//add w
  if(l==r){
    Add(node[cur].k1,mul(dt,w));
    Add(node[cur].b1,w);
    Add(node[cur].k2,w);
    Add(node[cur].b2,w);
    return;
  }
  if(pos<=mid) update(pos,ls,l,mid,w);
  else update(pos,rs,mid+1,r,w);
  node[cur]=merge(node[ls],node[rs]);
}
Node query(int L,int R,int cur,int l,int r){
  if(L<=l&&r<=R) return node[cur];
  if(L<=mid&&(!(mid<R)))return query(L,R,ls,l,mid);
  if((!(L<=mid))&&(mid<R))return query(L,R,rs,mid+1,r);
  return merge(query(L,R,ls,l,mid),query(L,R,rs,mid+1,r));
}
struct BIT{
  int b[MAXN];
  void update(int x,int w){
    for(int i=x;i<=n;i+=i&-i)Add(b[i],w);
  }
  int query(int x){
    int ret=0;
    for(int i=x;i;i-=i&-i)Add(ret,b[i]);
    return ret;
  }
}bit;

void answer(){
  int l,r;
  l=rd();r=rd();
  int ans=0;
  Add(ans,mul(A,add(bit.query(r),-bit.query(l-1))));
  Add(ans,mul(B,query(l,r,1,1,n).b2));
  out(ans);nextline();
}
void change(){
  int pos,wa,wb,w;
  pos=rd();wa=rd();wb=rd();
  w=mul(wa,inv(wb));
  bit.update(pos,-p[pos]);
  bit.update(pos,w);
  update(pos,1,1,n,-p[pos]);
  update(pos,1,1,n,w);
  p[pos]=w;
}
int main(){
  freopen("omeed.in","r",stdin);
  freopen("omeed.out","w",stdout);
  rd();
  n=rd();q=rd();ta=rd();tb=rd();A=rd();B=rd();
  t=mul(ta,inv(tb));
  dt=add(1,-t);
  int x,y;
  for(int i=1;i<=n;i++){
    x=rd();y=rd();
    p[i]=mul(x,inv(y));
    bit.update(i,p[i]);
  }
  build(1,n,1,1,n);
  for(int i=1;i<=q;i++){
    x=rd();
    if(x==1) answer();
    else change();
  }
}

转载于:https://www.cnblogs.com/ghostcai/p/9817260.html

内容概要:本文档主要展示了C语言中关于字符串处理、指针操作以及动态内存分配的相关代码示例。首先介绍了如何实现键值对(“key=value”)字符串的解析,包括去除多余空格和根据键获取对应值的功能,并提供了相应的测试用例。接着演示了从给定字符串中分离出奇偶位置字符的方法,并将结果分别存储到两个不同的缓冲区中。此外,还探讨了常量(const)修饰符在变量和指针中的应用规则,解释了不同类型指针的区别及其使用场景。最后,详细讲解了如何动态分配二维字符数组,并实现了对这类数组的排序与释放操作。 适合人群:具有C语言基础的程序员或计算机科学相关专业的学生,尤其是那些希望深入理解字符串处理、指针操作以及动态内存管理机制的学习者。 使用场景及目标:①掌握如何高效地解析键值对字符串并去除其中的空白字符;②学会编写能够正确处理奇偶索引字符的函数;③理解const修饰符的作用范围及其对程序逻辑的影响;④熟悉动态分配二维字符数组的技术,并能对其进行有效的排序和清理。 阅读建议:由于本资源涉及较多底层概念和技术细节,建议读者先复习C语言基础知识,特别是指针和内存管理部分。在学习过程中,可以尝试动手编写类似的代码片段,以便更好地理解和掌握文中所介绍的各种技巧。同时,注意观察代码注释,它们对于理解复杂逻辑非常有帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值