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. centos7 docker安装详解

    查看内核和操作系统版本[root@prod3 ~]# uname -r3.10.0-327.el7.x86_64[root@prod3 ~]# cat /etc/redhat-release Cent ...

  2. Java的Date和Time入门教程

    本文是一篇翻译文章,已取得原作者授权,原文地址是http://tutorials.jenkov.com/java-date-time/index.html Java语言的JDK中关于日期和时间的API ...

  3. 帝国CMS万能标签的使用

    标签名称: 带模板的信息调用标签[万能标签]   [ecmsinfo]栏目ID/专题ID,显示条数,标题截取数,是否显示栏目名,操作类型,标签模板ID,只显示有标题图片[/ecmsinfo] 说明:e ...

  4. IDEA使用心得-----懒得截图了,但是大家应该看得懂

    1.界面设置,有白色和 黑色风格两种,我个人喜欢黑色风格,护眼最重要的是看着帅. 设置方法:FILE--Settings--Editor--Colors&Fonts--Scheme name ...

  5. C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(中)

    译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(中)),不对的地方欢迎指出与交流. 章节出自<Professional C# ...

  6. javascript面向对象(一)

    javascript是弱类型,直译式的面相对象编程语言. 在之前我们说过 var a = 123: 在这里a是整数 但是我们可以给a重新复制为 a="你好"; 在这个过程中变量a ...

  7. fastlane安装流程和fastlane match同步证书和PP文件方法

    分步指南 安装fastlane:  ① Make sure you have the latest version of the Xcode command line tools installed: ...

  8. 初步认识Thymeleaf:简单表达式和标签。(二)

    本篇文章是对上篇文章中Thymeleaf标签的补充. 1.th:each:循环,<tr th:each="user,userStat:${users}">,userSt ...

  9. win8效果的横向布局

    有一个月没写过博客了,自己的博客也没有看过,前段时间一直在忙着写代码,公司有一个制漆的产品,与传统纵向布局不一样,要求页面横向布局,类似win8的那种布局效果,最开始,我也没有什么头绪,然后硬着头皮做 ...

  10. OC--Runtime知识点整理

    1.Runtime简介 因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时.也就是说只有编译器是不够的,还需要一个运行时系统 (runtime system) 来执行编译 ...