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 可以发现这个树状数组维护的是后缀和. 所以二维线 ...
随机推荐
- yii2 Nav::widget() 和 Menu::widget()
Nav::widget http://www.yiiframework.com/doc-2.0/yii-bootstrap-nav.html Menu::widget() http://www.yi ...
- Android 快速开发系列 ORMLite 框架最佳实践
比较靠谱的Helper的写法: 1.DatabaseHelper package com.zhy.zhy_ormlite.db; import java.sql.SQLException; impor ...
- 树链剖分-SPOJ375(QTREE)
QTREE - Query on a tree You are given a tree (an acyclic undirected connected graph) with N nodes, a ...
- mac环境下安装xampp
首先下载XAMPP,然后配置虚拟域名hosts,再配置Apache服务, 配置Apache服务 1.打开/Applications/XAMPP/xamppfiles/etc/httpd.conf文件, ...
- php基本数据类型需要注意的地方
一.布尔(Boolean) 手册中提到特殊类型NULL(包括尚未赋值的变量)会被换为false值,我自己在测试的时候发现NULL值可以转换为false,虽然false也会输出,但是尚未赋值的变量会报N ...
- 走入PHP-数据类型和字符串语法
PHP支持8种原始数据类型 四种标量类型: boolean | integer | float(as double) | string 两种复合类型: array | object 两种特殊类型 re ...
- windows_keyboard shortcuts快捷键
单独按Windows:显示或隐藏"开始"功能表 Windows+BREAK:显示"系统属性" 对话框 Windows+D:显示桌面 Windows+M:最小化所 ...
- JS——控制标记的样式
1.定义一个div,宽度为100px,高度为100px,背景色为粉色. 定义一个事件,鼠标移入时背景色变为蓝色,宽度变为200px. 定义一个事件,鼠标移出时背景色变为红色. html文件: < ...
- sleep()和wait()的区别 --- 快入睡了,突然想起一个知识点,搞完就睡
自从开了博客之后,大部分是转发的,不断的ctrl+c和ctrl+v,知识是越屯越多,吸收的却很少,后来越来越懒,直接保存链接了. 我已经认识到了自己的错误,自己查询到的这些知识,以后还是会定期保存的, ...
- 设计模式(三)—代理模式
目录: 一.概述 二.静态代理 三.动态代理 四.静态代理和动态代理的区别 一.概述 代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对 ...