Description

漆黑的晚上,九条可怜躺在床上辗转反侧。难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历。那是一道基础的树状数组题。给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种:

1 x,表示将 Ax 变成 (Ax + 1) mod 2。
2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r
尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做。当时非常young 的她写了如下的算法:
1: function Add(x)
2: while x > 0 do
3: A
x ← (Ax + 1) mod 2
4: x ← x ? lowbit(x)
5: end while
6: end function
7:
8: function Find(x)
9: if x == 0 then
10: return 0
11: end if
12: ans ← 0
13: while x ≤ n do
14: ans ← (ans + Ax) mod 2
15: x ← x + lowbit(x)
16: end while
17: return ans
18: end function
19:
20: function Query(l, r)
21: ansl ← Find(l ? 1)
22: ansr ← Find(r)
23: return (ansr ? ansl + 2) mod 2
24: end function
其中 lowbit(x) 表示数字 x 最?的非 0 二进制位,例如 lowbit(5) = 1, lowbit(12) = 4。进行第一类操作的时候就调用 Add(x),第二类操作的时候答案就是 Query(l, r)。如果你对树状数组比较熟悉,不难发现可怜把树状数组写错了: Add和Find 中 x 变化的方向反了。因此这个程序在最终测试时华丽的爆 0 了。然而奇怪的是,在当时,这个程序通过了出题人给出的大样例——这也是可怜没有进行对拍的原因。现在,可怜想要算一下,这个程序回答对每一个询问的概率是多少,这样她就可以再次的感受到自己是一个多么非的人了。然而时间已经过去了很多年,即使是可怜也没有办法完全回忆起当时的大样例。幸运的是,她回忆起了大部分内容,唯一遗忘的是每一次第一类操作的 x的值,因此她假定这次操作的 x 是在 [li, ri] 范围内 等概率随机 的。具体来说,可怜给出了一个长度为 n 的数组 A,初始为 0,接下来进行了 m 次操作:
1 l r,表示在区间 [l, r] 中等概率选取一个 x 并执行 Add(x)。
2 l r,表示询问执行 Query(l, r) 得到的结果是正确的概率是多少。

Input

第一行输入两个整数 n, m。
接下来 m 行每行描述一个操作,格式如题目中所示。
N<=10^5,m<=10^5,1<=L<=R<=N

Output

对于每组询问,输出一个整数表示答案。如果答案化为最简分数后形如 x/y,那么你只需要输出 x*y^?1 mod 998244353 后的值。(即输出答案模 998244353)。

Sample Input

5 5
1 3 3
2 3 5
2 4 5
1 1 3
2 2 5

Sample Output

1
0
665496236
在进行完 Add(3) 之后, A 数组变成了 [0, 1, 1, 0, 0]。所以前两次询问可怜的程序答案都是1,因此第一次询问可怜一定正确,第二次询问可怜一定错误。

正解:树套树(二维线段树)。

这道题好绕。。其实如果你打表,或者是深知树状数组的原理的话,你就可以发现,询问的时候其实是查询后缀和。

那么对于每个询问,如果$l!=1$,那么我们查询的其实是$[l-1,r-1]$这段区间。而$[l-1,r-1]$与$[l,r]$仅有$l-1$和$r$这两个元素有区别。所以我们每次询问就是问$l-1$和$r$的修改次数在模2意义下是否相等。

那么我们可以把每个询问看成$(l-1,r)$这个点,那么这就是个二维选点问题了,我们用树套树来维护。外层的树维护第一维坐标,内层的树维护第二维坐标。我们维护的值就是这个点的两个坐标修改次数在模2意义下相等的概率。

如何合并概率呢?当两个点被修改的概率分别为$p1,p2$时,两个点修改次数相等的概率是$p=p1*p2+(1-p1)*(1-p2)$,这个式子也适用于合并操作。

当$l=1$时,情况类似,我们要求的就变成了对于$r$这个点,它的前缀和是否与它的后缀和相等。维护思想与$l!=1$的情况类似。

这道题的修改操作有点复杂,我在代码里加一点注释吧。。

然后我被$uoj$的$hack$数据卡常了$woc$。。

 //It is made by wfj_2048~
#include <algorithm>
#include <iostream>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define rhl (998244353)
#define lson (x<<1)
#define rson (x<<1|1)
#define mid ((l+r)>>1)
#define inf (1<<30)
#define N (100010)
#define il inline
#define RG register
#define ll long long
#define merge(p1,p2) ( (p1*p2+(1-p1+rhl)*(1-p2+rhl))%rhl ) //合并操作
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) using namespace std; int ls[*N],rs[*N],rt[*N],sz; //二维线段树
ll sum[*N],ans;
int n,m; il int gi(){
RG int x=,q=; RG char ch=getchar();
while ((ch<'' || ch>'') && ch!='-') ch=getchar();
if (ch=='-') q=-,ch=getchar();
while (ch>='' && ch<='') x=x*+ch-,ch=getchar();
return q*x;
} //求逆元
il ll qpow(RG ll a,RG ll b){
RG ll ans=;
while (b){
if (b&) ans=ans*a;
a=a*a,b>>=;
if (ans>=rhl) ans%=rhl;
if (a>=rhl) a%=rhl;
}
return ans;
} //内层线段树修改
il void update(RG int &x,RG int l,RG int r,RG int xl,RG int xr,RG ll v){
if (!x) sum[x=++sz]=;
if (xl<=l && r<=xr){
sum[x]=merge(sum[x],v);
return;
}
if (xr<=mid) update(ls[x],l,mid,xl,xr,v);
else if (xl>mid) update(rs[x],mid+,r,xl,xr,v);
else update(ls[x],l,mid,xl,mid,v),update(rs[x],mid+,r,mid+,xr,v);
return;
} //内层线段树查询
il void query(RG int x,RG int l,RG int r,RG int p){
if (!x) return; ans=merge(ans,sum[x]); if (l==r) return;
p<=mid?query(ls[x],l,mid,p):query(rs[x],mid+,r,p); return;
} //外层线段树修改
il void Update(RG int x,RG int l,RG int r,RG int l1,RG int r1,RG int l2,RG int r2,RG ll v){
if (l1<=l && r<=r1){ update(rt[x],,n+,l2,r2,v); return; }
if (r1<=mid) Update(lson,l,mid,l1,r1,l2,r2,v);
else if (l1>mid) Update(rson,mid+,r,l1,r1,l2,r2,v);
else Update(lson,l,mid,l1,mid,l2,r2,v),Update(rson,mid+,r,mid+,r1,l2,r2,v);
return;
} //外层线段树查询
il void Query(RG int x,RG int l,RG int r,RG int p1,RG int p2){
if (rt[x]) query(rt[x],,n+,p2); if (l==r) return;
p1<=mid?Query(lson,l,mid,p1,p2):Query(rson,mid+,r,p1,p2);
return;
} il void work(){
n=gi(),m=gi(); RG int type,l,r;
for (RG int i=;i<=m;++i){
type=gi(),l=gi(),r=gi();
if (type==){
RG ll p=qpow(r-l+,rhl-);
//一个点被修改的概率为p=1/(r-l+1)
//l!=1的情况
if (l>) Update(,,n,,l-,l,r,(-p+rhl)%rhl);
if (r<n) Update(,,n,l,r,r+,n,(-p+rhl)%rhl);
//[1,l-1]与[l,r]和[l,r]与[r+1,n]之间,修改次数相等的概率为(1-p)
RG ll pp=p<<; if (pp>=rhl) pp-=rhl;
Update(,,n,l,r,l,r,(-pp+rhl)%rhl);
//[l+r]与[l+r]之间,修改次数相等的概率为1-2*p
//l==1的情况
Update(,,n,,,,l-,);
Update(,,n,,,r+,n+,);
//[0,l-1]和[r+1,n+1]这两个区间,前缀和等于后缀和的概率为0
Update(,,n,,,l,r,p);
//[l,r]这个区间,前缀和等于后缀和的概率为p
} else{
ans=,Query(,,n,l-,r);
//查询操作,即查询(l-1,r)修改次数相等的概率
//若l==1,即查询r的前缀和等于后缀和的概率
printf("%lld\n",ans);
}
}
return;
} int main(){
File("bit");
work();
return ;
}

bzoj4785 [Zjoi2017]树状数组的更多相关文章

  1. [BZOJ4785][ZJOI2017]树状数组(概率+二维线段树)

    4785: [Zjoi2017]树状数组 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 297  Solved: 195[Submit][Status ...

  2. BZOJ4785 ZJOI2017树状数组(概率+二维线段树)

    可以发现这个写挂的树状数组求的是后缀和.find(r)-find(l-1)在模2意义下实际上查询的是l-1~r-1的和,而本来要查询的是l~r的和.也就是说,若结果正确,则a[l-1]=a[r](mo ...

  3. BZOJ4785 [Zjoi2017]树状数组 【二维线段树 + 标记永久化】

    题目链接 BZOJ4785 题解 肝了一个下午QAQ没写过二维线段树还是很难受 首先题目中的树状数组实际维护的是后缀和,这一点凭分析或经验或手模观察可以得出 在\(\mod 2\)意义下,我们实际求出 ...

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

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

  5. 【BZOJ4785】[Zjoi2017]树状数组 树套树(二维线段树)

    [BZOJ4785][Zjoi2017]树状数组 Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一 ...

  6. 【bzoj4785】[Zjoi2017]树状数组 线段树套线段树

    题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作 ...

  7. [ZJOI2017]树状数组

    Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道 基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来 ...

  8. LOJ2251 [ZJOI2017] 树状数组【线段树】【树套树】

    题目分析: 对于一个$add$操作,它的特点是与树状数组的查询相同,会给$1$到它自己产生影响,而$query$操作则会途径所有包含它的树状数组点.现在$add$操作具有前向性(不会影响之后的点).所 ...

  9. 【uoj291】 ZJOI2017—树状数组

    http://uoj.ac/problem/291 (题目链接) 题意 一个写错的树状数组有多大的概率与正常树状数组得出的答案一样. Solution 可以发现这个树状数组维护的是后缀和. 所以二维线 ...

随机推荐

  1. wxpython tab切换页面

    最近没事学习下wxpython,发现很少有关于页面切换的demo,这边分享下2中切换的方法.第一种:利用wx.Notebook第二种:利用Sizer布局实现(自己写的),代码没有涉及到什么重构之类的优 ...

  2. java初级开发一系列的工具安装配置

    最近压力很大,刚刚帮朋友安装了一整套环境,按照印象写下此篇记录(没有图解),一是用来释放压力,二来希望可以帮到各位. 一.首先,Eclipse的安装,下载eclipse压缩包,一般为200~300M, ...

  3. 使用jQuery监听扫码枪输入并禁止手动输入的实现方法

    @(知识点总结)[jquery|扫码抢] 基于jQuery的扫码枪监听.如果只是想实现监听获取条码扫码信息,可以直接拿来使用,如果有更多的条码判断处理逻辑需要自己扩展. 一.功能需求 使用扫码枪扫描条 ...

  4. 源码分析Android Handler是如何实现线程间通信的

    源码分析Android Handler是如何实现线程间通信的 Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的.开发者从一开始就被告知必须在主线程中进行UI操作.但H ...

  5. 【树莓派】Linux应用相关:自动删除n天前日志

    linux是一个很能自动产生文件的系统,日志.邮件.备份等.虽然现在硬盘廉价,我们可以有很多硬盘空间供这些文件浪费,让系统定时清理一些不需要的文件很有一种爽快的事情.不用你去每天惦记着是否需要清理日志 ...

  6. IOS百度地图之--->第一篇《环境配置与基本使用》

    Ios 百度地图SDK简易使用说明:http://developer.baidu.com/map/index.php?title=iossdk 先道歉:对于原来上传的Demo我很抱歉,什么都没有,也没 ...

  7. Modbus通信协议的压力测试

    最近物联网都比较的火,因此,特别为各位兄弟姐妹们,奉上一款Mobus协议的测试软件,可以用来做设备的压力测试,和通信测试. 起初软件开发缘由是我们最近在开发一款设备,需要将多个DS18B20并联起来, ...

  8. Servlet实现的三种方式

    实现Servlet的三种方式:一个实现,两个继承 /*========================================== * servlet的执行过程: * 1.创建servlet对 ...

  9. Python之路-操作系统&网络基础

    一.为何要有操作系统 没有操作系统的话,计算机同样可以运行,但是程序员要了解到计算机底层各种各样的细节,而操作系统聪明地封装起来了底层这些繁杂的操作,通过向程序员开放一个个的接口,来最终使我们实现对底 ...

  10. Android线程池使用终结版

    有一段时间没写博文了,今天抽空总结一下,也希望能通过自己写的这些文章,加深理解的同时能帮 助在技术方面有疑点的朋友搞清楚个所以然来,由于经常会在网上或群里看到有朋友会问线程方面的 东西,就像我一个朋友 ...