bzoj4785 [Zjoi2017]树状数组
Description
漆黑的晚上,九条可怜躺在床上辗转反侧。难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历。那是一道基础的树状数组题。给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种:
Input
Output
Sample Input
1 3 3
2 3 5
2 4 5
1 1 3
2 2 5
Sample Output
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]树状数组的更多相关文章
- [BZOJ4785][ZJOI2017]树状数组(概率+二维线段树)
4785: [Zjoi2017]树状数组 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 297 Solved: 195[Submit][Status ...
- BZOJ4785 ZJOI2017树状数组(概率+二维线段树)
可以发现这个写挂的树状数组求的是后缀和.find(r)-find(l-1)在模2意义下实际上查询的是l-1~r-1的和,而本来要查询的是l~r的和.也就是说,若结果正确,则a[l-1]=a[r](mo ...
- BZOJ4785 [Zjoi2017]树状数组 【二维线段树 + 标记永久化】
题目链接 BZOJ4785 题解 肝了一个下午QAQ没写过二维线段树还是很难受 首先题目中的树状数组实际维护的是后缀和,这一点凭分析或经验或手模观察可以得出 在\(\mod 2\)意义下,我们实际求出 ...
- bzoj4785:[ZJOI2017]树状数组:二维线段树
分析: "如果你对树状数组比较熟悉,不难发现可怜求的是后缀和" 设数列为\(A\),那么可怜求的就是\(A_{l-1}\)到\(A_{r-1}\)的和(即\(l-1\)的后缀减\( ...
- 【BZOJ4785】[Zjoi2017]树状数组 树套树(二维线段树)
[BZOJ4785][Zjoi2017]树状数组 Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一 ...
- 【bzoj4785】[Zjoi2017]树状数组 线段树套线段树
题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作 ...
- [ZJOI2017]树状数组
Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道 基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来 ...
- LOJ2251 [ZJOI2017] 树状数组【线段树】【树套树】
题目分析: 对于一个$add$操作,它的特点是与树状数组的查询相同,会给$1$到它自己产生影响,而$query$操作则会途径所有包含它的树状数组点.现在$add$操作具有前向性(不会影响之后的点).所 ...
- 【uoj291】 ZJOI2017—树状数组
http://uoj.ac/problem/291 (题目链接) 题意 一个写错的树状数组有多大的概率与正常树状数组得出的答案一样. Solution 可以发现这个树状数组维护的是后缀和. 所以二维线 ...
随机推荐
- 面向对象的全套“企业微信”api接口的代码实现,网上太多“面向过程”微信api接口的代码,这个开源给需要的人用
有段时间没有写文章了. 一直以来,微信的热门是看得到的,很多人都需要与微信的api对接. 今天我这里就分享全套的企业微信api接口的代码. 关于微信api,网上已经有很多实现的了. 但是我今天之所以还 ...
- canvas的使用
1.概念 canvas一般就是用来绘制图像的 2.基本知识 上下文对象 var canvas = doucment.getElementById("canvas") ...
- Windows7 java-jdk1.7安装及设置变量过程
jdk安装的次数较少,容易忘记,这里专门记录一下. 1:jdk1.7网上到处都是可以随便下载一个.然后进行安装,不过在安装过程中把安装路径单独记忆一下,在配置变量的时候会用到. 2:安装完JDK后配置 ...
- 关于struts2中的default-action-ref
struts2中的default-action-ref一般用于,在请求无效或错误时将请求指引到错误页面.我这次的用法是在请求首页之前先发送请求到后台,进行数据获取后再转至首页显示,但是出了一个问题,d ...
- fastjson过滤不需要的属性
以下是一个通用的对象转json的方法,使用的fastjson的SimplePropertyPreFilter 对象,个人感觉比使用PropertyPreFilter的匿名内部类形式的过滤器更好用!直接 ...
- Web Storage
前面的话 Web存储最初作为HTML5的一部分被定义成API形式,但是后来被剥离出来作为独立的一份标准了.该标准目前还在草案阶段,但其中一部分内容已经被包括IE8在内的所有主流浏览器(可交互地)实现了 ...
- 浅谈隐语义模型和非负矩阵分解NMF
本文从基础介绍隐语义模型和NMF. 隐语义模型 ”隐语义模型“常常在推荐系统和文本分类中遇到,最初来源于IR领域的LSA(Latent Semantic Analysis),举两个case加快理解. ...
- 百度api的使用
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- Unity 3D Framework Designing(4)——设计可复用的SubView和SubViewModel(Part 2)
在我们设计和开发应用程序时,经常要用到控件.比如开发一个客户端WinForm应用程序时,微软就为我们提供了若干控件,这些控件为我们提供了可被定制的属性和事件.属性可以更改它的外观,比如背景色,标题等, ...
- 在docker container中运行docker-compose
为了保持宿主主机的环境干净,因此将docker-compose安装到一个基于centos7.3的容器之中,因为tianchao屏蔽了amazonaws,最后选择了通过pip方式来安装,这也是官方推荐的 ...