题面传送门

首先学过树状数组的应该都知道,将树状数组方向写反等价于前缀和 \(\to\) 后缀和,因此题目中伪代码的区间求和实质上是 \(sum[l-1...n]-sum[r...n]=sum[l-1...r-1]\),我们要求 \(sum[l...r]=sum[l-1...r-1]\) 的概率,等价于求 \(a_{l-1}=a_r\) 的概率。

因此我们可将题目转化为,每次从 \([l,r]\) 中随机选择一个数将其状态翻转,并询问 \(a_x=a_y\) 的概率。

这个可以通过二维线段树解决。建一棵二维线段树,第 \(x\) 行第 \(y\) 个位置上的值 \(p_{x,y}\) 表示 \(a_x=a_y\) 的概率。考虑一次修改 \([l,r]\) 对 \(p_{i,j}\) 的影响,分三种情况:

  • \(i\in [1,l-1],j\in[l,r]\),\(a_i\) 不会发生变化,\(a_j\) 有 \(p_1=\dfrac{1}{r-l+1}\) 的概率发生变化,故 \(p_{i,j}=(1-p_{i,j})\times p_1+p_{i,j}\times(1-p_1)\)。
  • \(i\in [l,r],j\in[l,r]\),\(a_i,a_j\) 各有 \(\dfrac{1}{r-l+1}\) 的概率发生变化,发生变化的总概率 \(p_2=\dfrac{2}{r-l+1}\),故 \(p_{i,j}=(1-p_{i,j})\times p_2+p_{i,j}\times(1-p_2)\)。
  • \(i\in [l,r],j\in[r+1,n]\),\(a_j\) 不会发生变化,\(a_i\) 有 \(p_3=\dfrac{1}{r-l+1}\) 的概率发生变化,故 \(p_{i,j}=(1-p_{i,j})\times p_3+p_{i,j}\times(1-p_3)\)。

对于这三种情况,相当于是对二维线段树上一个矩形执行 \(p_{i,j}\leftarrow (1-p_{i,j})\times P+p_{i,j}\times (1-P)\),这就直接在内层线段树上的区间上打一个 \(P\) 的标记。当合并两个标记 \(P,Q\) 时候,令新的标记 \(R=(1-P)\times Q+(1-Q)\times P\)。由于标记不能下放,因此需要标记永久化,时间复杂度线性二次对数。

当然你可能会有疑惑,上面三种情况中没有考虑 \(i\in [r+1,n],j\in[l,r]\) 的情况,当 \(i=j\) 时候 \(p_{i,j}\) 发生的变化的概率应当为 \(0\),也不是所谓的 \(\dfrac{2}{r-l+1}\),为什么算出来的概率还是正确的呢?事实上,我们查询的时候一定有 \(l-1<r\),因此我们只需维护 \(i<j\) 的 \(p_{i,j}\) 的值即可, 上述写法只不过更方便我们执行二维线段树上的矩形加罢了,不会对算法正确性产生影响。

最后特判 \(l=1\) 的情况,当执行的操作次数为奇数的时候,\(sum[l-1...n]\) 的真实值应当为 \(1\),而伪代码为了确保不卡入死循环直接返回了 \(0\),因此若执行的操作次数为奇数,我们要求的实质上是 \(a_{l-1}\ne a_r\) 的概率。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const int MOD=998244353;
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,qu,ncnt=0;
struct node{int ch[2],tag;} s[MAXN*500+5];
int rt[MAXN*4+5];
void modify_in(int &k,int l,int r,int ql,int qr,int p){
if(!k){k=++ncnt;s[k].tag=1;}
if(ql<=l&&r<=qr){
s[k].tag=(1ll*p*s[k].tag+1ll*(1+MOD-p)*(1+MOD-s[k].tag))%MOD;
return;
} int mid=l+r>>1;
if(qr<=mid) modify_in(s[k].ch[0],l,mid,ql,qr,p);
else if(ql>mid) modify_in(s[k].ch[1],mid+1,r,ql,qr,p);
else modify_in(s[k].ch[0],l,mid,ql,mid,p),modify_in(s[k].ch[1],mid+1,r,mid+1,qr,p);
}
void modify_out(int k,int l,int r,int ql,int qr,int qx,int qy,int p){
if(ql<=l&&r<=qr){modify_in(rt[k],1,n,qx,qy,p);return;}
int mid=l+r>>1;
if(qr<=mid) modify_out(k<<1,l,mid,ql,qr,qx,qy,p);
else if(ql>mid) modify_out(k<<1|1,mid+1,r,ql,qr,qx,qy,p);
else modify_out(k<<1,l,mid,ql,mid,qx,qy,p),modify_out(k<<1|1,mid+1,r,mid+1,qr,qx,qy,p);
}
int query_in(int k,int l,int r,int p){
if(!k) return 1;if(l==r) return s[k].tag;
int mid=l+r>>1,pp=(p<=mid)?query_in(s[k].ch[0],l,mid,p):query_in(s[k].ch[1],mid+1,r,p);
return (1ll*pp*s[k].tag+1ll*(MOD+1-pp)*(MOD+1-s[k].tag))%MOD;
}
int query_out(int k,int l,int r,int p,int q){
if(l==r) return query_in(rt[k],1,n,q);
int mid=l+r>>1;
int p1=(p<=mid)?query_out(k<<1,l,mid,p,q):query_out(k<<1|1,mid+1,r,p,q);
int p2=query_in(rt[k],1,n,q);
return (1ll*p1*p2+1ll*(MOD+1-p1)*(MOD+1-p2))%MOD;
}
int main(){
scanf("%d%d",&n,&qu);int cnt=0;
while(qu--){
int opt,l,r;scanf("%d%d%d",&opt,&l,&r);
if(opt==1){
int p=qpow(r-l+1,MOD-2);cnt^=1;
modify_out(1,0,n,0,l-1,l,r,(MOD+1-p)%MOD);
if(r^n) modify_out(1,0,n,l,r,r+1,n,(MOD+1-p)%MOD);
modify_out(1,0,n,l,r,l,r,(MOD+1-2*p%MOD)%MOD);
} else {
int ret=query_out(1,0,n,l-1,r);
if(!(l^1)&&cnt) ret=(MOD+1-ret)%MOD;
printf("%d\n",ret);
}
}
return 0;
}

洛谷 P3688 - [ZJOI2017]树状数组(二维线段树+标记永久化)的更多相关文章

  1. bzoj4785:[ZJOI2017]树状数组:二维线段树

    分析: "如果你对树状数组比较熟悉,不难发现可怜求的是后缀和" 设数列为\(A\),那么可怜求的就是\(A_{l-1}\)到\(A_{r-1}\)的和(即\(l-1\)的后缀减\( ...

  2. BZOJ 4785 [Zjoi2017]树状数组 | 二维线段树

    题目链接 BZOJ 4785 题解 这道题真是令人头秃 = = 可以看出题面中的九条可怜把求前缀和写成了求后缀和,然后他求的区间和却仍然是sum[r] ^ sum[l - 1],实际上求的是闭区间[l ...

  3. 树状数组 二维偏序【洛谷P3431】 [POI2005]AUT-The Bus

    P3431 [POI2005]AUT-The Bus Byte City 的街道形成了一个标准的棋盘网络 – 他们要么是北南走向要么就是西东走向. 北南走向的路口从 1 到 n编号, 西东走向的路从1 ...

  4. 洛谷 P1972 [SDOI2009]HH的项链-二维偏序+树状数组+读入挂(离线处理,思维,直接1~n一边插入一边查询),hahahahahahaha~

    P1972 [SDOI2009]HH的项链 题目背景 无 题目描述 HH 有一串由各种漂亮的贝壳组成的项链.HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含 ...

  5. BZOJ4822[Cqoi2017]老C的任务——树状数组(二维数点)

    题目描述 老 C 是个程序员.     最近老 C 从老板那里接到了一个任务——给城市中的手机基站写个管理系统.作为经验丰富的程序员,老 C 轻松 地完成了系统的大部分功能,并把其中一个功能交给你来实 ...

  6. BZOJ1935: [Shoi2007]Tree 园丁的烦恼(树状数组 二维数点)

    题意 题目链接 Sol 二维数点板子题 首先把询问拆成四个矩形 然后离散化+树状数组统计就可以了 // luogu-judger-enable-o2 #include<bits/stdc++.h ...

  7. 树状数组+二维前缀和(A.The beautiful values of the palace)--The Preliminary Contest for ICPC Asia Nanjing 2019

    题意: 给你螺旋型的矩阵,告诉你那几个点有值,问你某一个矩阵区间的和是多少. 思路: 以后记住:二维前缀和sort+树状数组就行了!!!. #define IOS ios_base::sync_wit ...

  8. bzoj 4822: [Cqoi2017]老C的任务【扫描线+树状数组+二维差分】

    一个树状数组能解决的问题分要用树套树--还写错了我别是个傻子吧? 这种题还是挺多的,大概就是把每个矩形询问差分拆成四个点前缀和相加的形式(x1-1,y1-1,1)(x2.y2,1)(x1-1,y2,- ...

  9. 【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询

    Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位 ...

  10. [Usaco2014 Open Gold ]Cow Optics (树状数组+扫描线/函数式线段树)

    这道题一上手就知道怎么做了= = 直接求出原光路和从目标点出发的光路,求这些光路的交点就行了 然后用树状数组+扫描线或函数式线段树就能过了= = 大量的离散+模拟+二分什么的特别恶心,考试的时候是想到 ...

随机推荐

  1. 【UE4 C++ 基础知识】<12> 多线程——FRunnable

    概述 UE4里,提供的多线程的方法: 继承 FRunnable 接口创建单个线程 创建 AsyncTask 调用线程池里面空闲的线程 通过 TaskGraph 系统来异步完成一些自定义任务 支持原生的 ...

  2. 第五课第四周笔记2:Self-Attention 自注意力

    Self-Attention 自注意力 让我们跳进去谈谈transformer的self-attention机制.如果您能了解本视频背后的主要思想,您就会了解变压器网络工作背后最重要的核心思想. 让我 ...

  3. Coursera Deep Learning笔记 序列模型(二)NLP & Word Embeddings(自然语言处理与词嵌入)

    参考 1. Word Representation 之前介绍用词汇表表示单词,使用one-hot 向量表示词,缺点:它使每个词孤立起来,使得算法对相关词的泛化能力不强. 从上图可以看出相似的单词分布距 ...

  4. 5.31日 Scrum Metting

    日期:2021年5月31日 会议主要内容概述:讨论草稿箱前后端接口,讨论账单页面设计. 一.进度情况 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中遇到的困难 徐宇龙 后端 文件导入功能 ...

  5. WiFi天线对PCB布局布线和结构的要求详解 - 全文

    随着市场竞争的加剧,硬件设备正以集成化的方向发展.天线也由外置进化内置再进化到嵌入式,我们先来介绍这类应用的天线种类: ⑴ On Board板载式:采用PCB蚀刻一体成型,性能受限,极低成本,应用于蓝 ...

  6. MySQL 的架构与组件

    MySQL 的逻辑架构图设计图 连接/线程处理:管理客户端连接/会话[mysql threads] 解析器:通过检查SQL查询中的每个字符来检查SQL语法,并为每个SQL查询生成  SQL_ID. 此 ...

  7. InnoDB存储引擎的锁

    InnoDB存储引擎的锁 锁的类型 锁的类型包括: 1.     共享锁(S lock),允许事务读取一行数据 2.     排他锁(X lock),允许事务删除或更新一行数据 锁的兼容性a X S ...

  8. 【mysql2】下载安装mysql5.7版|不再更新系列

    一.下载MySQL 5.7 版 MySQL 5.7 版:官网下载地址 https://dev.mysql.com/downloads/windows/installer/5.7.html 下载的是50 ...

  9. 2021 ICPC 江西省赛总结

      比赛链接:https://ac.nowcoder.com/acm/contest/21592   大三的第一场正式赛,之前的几次网络赛和选拔赛都有雄哥坐镇,所以并没有觉得很慌毕竟校排只取每个学校成 ...

  10. QuantumTunnel:Netty实现

    接上一篇文章内网穿透服务设计挖的坑,本篇来聊一下内网穿透的实现. 为了方便理解,我们先统一定义使用到的名词: UserClient:用户客户端,真实的请求发起方: UserServer:内网穿透-用户 ...