P3688 [ZJOI2017] 树状数组 【二维线段树】
题目描述:这里有一个写挂的树状数组:

有两种共\(m\)个操作:
输入\(l,r\),在\([l,r]\)中随机选择一个整数\(x\)执行\(\text{Add}(x)\)
输入\(l,r\),询问执行\(\text{Query}(l,r)\)的答案正确的概率\(\text{mod} \ 998244353\)。
数据范围:\(n,m\leq 100000\)
首先,根据这个代码,我们知道这就是一个单点修改求后缀和的数据结构。所以\(\text{Query}(l,r)\)求的是\([l-1,r-1]\)的和。所以正确当且仅当\(a_{l-1}=a_r\)。
注意,如果你直接维护每个数为\(0,1\)的概率,就会出现可能多次修改的问题。所以要\((x,y)\)维护\(a_x\neq a_y\)的概率。我们知道\(a_x\neq a_y\)的概率为\(p\),并以\(q\)的概率修改,则之后的概率为\(p(1-q)+q(1-p)\),这个标记是可合并的。所以:
- 若\(x\in [1,l-1],y\in [l,r]\)或\(x\in [l,r],y\in [r+1,n]\),则以\(\frac{1}{r-l+1}\)的概率修改。
- 若\(x,y\in [l,r]\),则以\(\frac{2}{r-l+1}\)的概率修改。
注意,上面\(\text{Find}(x)\)特判了\(x=0\)的情况,所以若\(l=1\)则计算的是\(r\)的后缀和,所以用\((0,x)\)维护\(x\)的前缀和是否等于\(x\)的后缀和的概率。
- 若\(x\notin [l,r]\),则以1的概率修改。
- 若\(x\in [l,r]\),则以\(\frac{r-l}{r-l+1}\)的概率修改。
而且询问是单点询问,所以我们可以使用二维线段树维护并标记永久化。
如果你不想卡常并最大点使用时限\(-0.01s\)通过Luogu评测,而且不能在UOJ通过,可以使用cdq分治,但是我不会。。。
#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = 100003, mod = 998244353;
inline int kasumi(int a, int b){
int res = 1;
while(b){
if(b & 1) res = (LL) res * a % mod;
a = (LL) a * a % mod; b >>= 1;
}
return res;
}
int n, m, inv[N], root[N << 2], val[N * 350], ls[N * 350], rs[N * 350], cnt;
inline int Add(int a, int b){return (a + b >= mod) ? (a + b - mod) : (a + b);}
inline int Sub(int a, int b){return (a < b) ? (a + mod - b) : (a - b);}
inline int add(int a, int b){return Add((LL) a * Sub(1, b) % mod, (LL) b * Sub(1, a) % mod);}
inline void change(int &x, int L, int R, int l, int r, int v){
if(!x) x = ++ cnt;
if(l <= L && R <= r){val[x] = add(val[x], v); return;}
int mid = L + R >> 1;
if(l <= mid) change(ls[x], L, mid, l, r, v);
if(mid < r) change(rs[x], mid + 1, R, l, r, v);
}
inline int query(int x, int L, int R, int p){
if(!x) return 0;
if(L == R) return val[x];
int mid = L + R >> 1;
if(p <= mid) return add(val[x], query(ls[x], L, mid, p));
else return add(val[x], query(rs[x], mid + 1, R, p));
}
inline void change(int x, int L, int R, int l1, int r1, int l2, int r2, int v){
if(l1 <= L && R <= r1){change(root[x], 1, n, l2, r2, v); return;}
int mid = L + R >> 1;
if(l1 <= mid) change(x << 1, L, mid, l1, r1, l2, r2, v);
if(mid < r1) change(x << 1 | 1, mid + 1, R, l1, r1, l2, r2, v);
}
inline int query(int x, int L, int R, int p1, int p2){
if(L == R) return query(root[x], 1, n, p2);
int mid = L + R >> 1;
if(p1 <= mid) return add(query(root[x], 1, n, p2), query(x << 1, L, mid, p1, p2));
else return add(query(root[x], 1, n, p2), query(x << 1 | 1, mid + 1, R, p1, p2));
}
int main(){
scanf("%d%d", &n, &m);
for(Rint i = 1;i <= n;i ++) inv[i] = kasumi(i, mod - 2);
while(m --){
int opt, l, r;
scanf("%d%d%d", &opt, &l, &r);
if(opt == 1){
if(l > 1){
change(root[0], 1, n, 1, l - 1, 1);
change(1, 1, n, 1, l - 1, l, r, inv[r - l + 1]);
}
if(r < n){
change(root[0], 1, n, r + 1, n, 1);
change(1, 1, n, l, r, r + 1, n, inv[r - l + 1]);
}
if(l < r) change(1, 1, n, l, r, l, r, 2ll * inv[r - l + 1] % mod);
change(root[0], 1, n, l, r, Sub(1, inv[r - l + 1]));
} else if(l == 1) printf("%d\n", Sub(1, query(root[0], 1, n, r)));
else printf("%d\n", Sub(1, query(1, 1, n, l - 1, r)));
}
}
P3688 [ZJOI2017] 树状数组 【二维线段树】的更多相关文章
- bzoj4785:[ZJOI2017]树状数组:二维线段树
分析: "如果你对树状数组比较熟悉,不难发现可怜求的是后缀和" 设数列为\(A\),那么可怜求的就是\(A_{l-1}\)到\(A_{r-1}\)的和(即\(l-1\)的后缀减\( ...
- BZOJ 4785 [Zjoi2017]树状数组 | 二维线段树
题目链接 BZOJ 4785 题解 这道题真是令人头秃 = = 可以看出题面中的九条可怜把求前缀和写成了求后缀和,然后他求的区间和却仍然是sum[r] ^ sum[l - 1],实际上求的是闭区间[l ...
- BZOJ4822[Cqoi2017]老C的任务——树状数组(二维数点)
题目描述 老 C 是个程序员. 最近老 C 从老板那里接到了一个任务——给城市中的手机基站写个管理系统.作为经验丰富的程序员,老 C 轻松 地完成了系统的大部分功能,并把其中一个功能交给你来实 ...
- BZOJ1935: [Shoi2007]Tree 园丁的烦恼(树状数组 二维数点)
题意 题目链接 Sol 二维数点板子题 首先把询问拆成四个矩形 然后离散化+树状数组统计就可以了 // luogu-judger-enable-o2 #include<bits/stdc++.h ...
- 树状数组 二维偏序【洛谷P3431】 [POI2005]AUT-The Bus
P3431 [POI2005]AUT-The Bus Byte City 的街道形成了一个标准的棋盘网络 – 他们要么是北南走向要么就是西东走向. 北南走向的路口从 1 到 n编号, 西东走向的路从1 ...
- 树状数组+二维前缀和(A.The beautiful values of the palace)--The Preliminary Contest for ICPC Asia Nanjing 2019
题意: 给你螺旋型的矩阵,告诉你那几个点有值,问你某一个矩阵区间的和是多少. 思路: 以后记住:二维前缀和sort+树状数组就行了!!!. #define IOS ios_base::sync_wit ...
- bzoj 4822: [Cqoi2017]老C的任务【扫描线+树状数组+二维差分】
一个树状数组能解决的问题分要用树套树--还写错了我别是个傻子吧? 这种题还是挺多的,大概就是把每个矩形询问差分拆成四个点前缀和相加的形式(x1-1,y1-1,1)(x2.y2,1)(x1-1,y2,- ...
- 【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询
Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位 ...
- [Usaco2014 Open Gold ]Cow Optics (树状数组+扫描线/函数式线段树)
这道题一上手就知道怎么做了= = 直接求出原光路和从目标点出发的光路,求这些光路的交点就行了 然后用树状数组+扫描线或函数式线段树就能过了= = 大量的离散+模拟+二分什么的特别恶心,考试的时候是想到 ...
- HDU - 1166 树状数组模板(线段树也写了一遍)
题意: 汉语题就不说题意了,用到单点修改和区间查询(树状数组和线段树都可以) 思路: 树状数组的单点查询,单点修改和区间查询. 树状数组是巧妙运用二进制的规律建树,建树就相当于单点修改.这里面用到一个 ...
随机推荐
- interface Part3(实现:显示和隐式)
1. 接口的实现实际上和类之间的继承是一样的,也是重写了接口中的方法,让其有了具体的实现内容. 2. 但需要注意的是,在类中实现一个接口时必须将接口中的所有成员都实现,否则该类必须声明为抽象类,并将接 ...
- 笔记 - C#从头开始构建编译器 - 2
视频与PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-02.md 作者是 Immo Landwerth(https:// ...
- Ubuntu install android studio
Ubuntu install android studio 1. 安装 openjdk8,并在配置文件 /etc/profile 中,追加如下内容: sudo aptitude install ope ...
- Python初识面向对象
初识面向对象 面向过程 VS 面向对象 面向过程的圣墟设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西 优点是:极大的降低 ...
- java读取文件的几种方式性能比较
//普通输入流读取文件内容 public static long checksumInputStream(Path filename) { try(InputStream in= Files.newI ...
- 关于INT_MIN
来自为知笔记(Wiz)
- Leaflet 调用百度瓦片地图服务
在使用 leaflet 调用第三方瓦片地图服务的项目,主要谷歌地图.高德地图.百度地图和 OSM 地图,与其他三种地图对比,百度地图的瓦片组织方式是不同的.百度从中心点经纬度(0,0)度开始计算瓦片, ...
- Java中关于String类型的一些思考
作为初学者在学习Java的时候,变量类型是不可避免会遇到的,在以往我们的印象中字符串String都是作为基本类型而存在的,但是在Java中String类型确是一个实实在在的引用类型,是可以通过new关 ...
- C#一些不太熟悉的类——扩展学习
Process.CloseMainWindow Method 通过向进程的主窗口发送关闭消息来关闭拥有用户界面的进程. 注解 进程执行时,其消息循环处于等待状态. 每次操作系统将 Windows 消息 ...
- 虚拟dom应用
vdom如何应用,核心api是什么 1.介绍snabbdom(开源社区用的多,vue2用的是他) 首先回顾下之前的vdom格式 真实的dom <body> <ul id=" ...