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 可以发现这个树状数组维护的是后缀和. 所以二维线 ...
随机推荐
- centos7 docker安装详解
查看内核和操作系统版本[root@prod3 ~]# uname -r3.10.0-327.el7.x86_64[root@prod3 ~]# cat /etc/redhat-release Cent ...
- Java的Date和Time入门教程
本文是一篇翻译文章,已取得原作者授权,原文地址是http://tutorials.jenkov.com/java-date-time/index.html Java语言的JDK中关于日期和时间的API ...
- 帝国CMS万能标签的使用
标签名称: 带模板的信息调用标签[万能标签] [ecmsinfo]栏目ID/专题ID,显示条数,标题截取数,是否显示栏目名,操作类型,标签模板ID,只显示有标题图片[/ecmsinfo] 说明:e ...
- IDEA使用心得-----懒得截图了,但是大家应该看得懂
1.界面设置,有白色和 黑色风格两种,我个人喜欢黑色风格,护眼最重要的是看着帅. 设置方法:FILE--Settings--Editor--Colors&Fonts--Scheme name ...
- C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(中)
译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(中)),不对的地方欢迎指出与交流. 章节出自<Professional C# ...
- javascript面向对象(一)
javascript是弱类型,直译式的面相对象编程语言. 在之前我们说过 var a = 123: 在这里a是整数 但是我们可以给a重新复制为 a="你好"; 在这个过程中变量a ...
- fastlane安装流程和fastlane match同步证书和PP文件方法
分步指南 安装fastlane: ① Make sure you have the latest version of the Xcode command line tools installed: ...
- 初步认识Thymeleaf:简单表达式和标签。(二)
本篇文章是对上篇文章中Thymeleaf标签的补充. 1.th:each:循环,<tr th:each="user,userStat:${users}">,userSt ...
- win8效果的横向布局
有一个月没写过博客了,自己的博客也没有看过,前段时间一直在忙着写代码,公司有一个制漆的产品,与传统纵向布局不一样,要求页面横向布局,类似win8的那种布局效果,最开始,我也没有什么头绪,然后硬着头皮做 ...
- OC--Runtime知识点整理
1.Runtime简介 因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时.也就是说只有编译器是不够的,还需要一个运行时系统 (runtime system) 来执行编译 ...