【BZOJ4785】[Zjoi2017]树状数组 树套树(二维线段树)
【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,因此第一次询问可怜一定正确,第二次询问可怜一定错误。
题解:发现这里给的树状数组的方向正好是反过来的,也就是说这里的树状数组维护的实际上是后缀xor和。那么后缀xor和与前缀xor和相等的情况就是:
[1,l-1]^[1,r]=[l-1,n]^[r,n] --> [l,r]=[l-1,r-1] ---> [l-1]=[r]
也就是说我们求的是l的值和r的值相等的概率。然后到这里,大部分题解都说“这变成了一个二维数点问题”,然而本蒟蒻一脸mengbi,所以,这里还是换一种方法讲吧。
我们用(a,b)表示a的值和b的值相等的概率。加入我们想要修改[l,r]中随机一个点,那么我们先考虑所有l<=a<b<=r的点对。
对于点对a,b,一次修改中它们最多只有一个数改变,我们设$q=1-{2\over r-l+1}$,表示a,b相等性不变的概率,设p表示原来a,b相等的概率,那么$p=pq+(1-p)(1-q)$。并且,我们要对[l,r]中所有的点对都进行这个计算,那么我们可以认为(a,b)是二维平面上的一个点,我们要修改的是(l,l)-(r,r)这个矩形,这可以用二维线段树维护。
//问题:对于某个树上的节点x,我们先给它打了个标记q1,有想给它打个标记q2,这两个标记该如何处理呢?自己推一推就知道,因为一开始的p都是1,那么先处理q1和先处理q2的结果是相同的(也就是说标记满足交换律),设p打了q1标记变成p',我们在同样的给p'打个q2标记就行了。
再考虑a<l<=b<=r的点对(l<=a<=r<b的类似),这样的点对的相等性不变的概率就是$q=1-{1\over r-l+1}$。此时我们要修改的矩形就变成了(1,l-1)-(l,r),依旧二维线段树。
突然发现一种情况,当l=1时怎么办?因为l-1=0,所以此时要求的就是后缀xor和与前缀xor和相等的概率,单独维护一下就好了。
=======下面是二维线段树部分=======
本题要支持什么操作呢?矩形区间计算。因为二维线段树必须标记永久化,所以我们在第一位线段树时,每访问到一个合法的整区间,就进入第二维线段树去进行区间修改。这样,在查询的时候,我们的答案要将第一维线段树上 从根到叶子的所有节点的查询结果 全都算到一起,也就是说没经过一个节点就要更新答案。
#include <cstdio>
#include <cstring>
#include <iostream>
#define z(_) (((_)%mod+mod)%mod)
#define lson x<<1
#define rson x<<1|1
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int maxn=100010;
int n,m,tot;
ll inv(ll x)
{
ll z=1,y=mod-2;
while(y)
{
if(y&1) z=z*x%mod;
x=x*x%mod,y>>=1;
}
return z;
}
ll calc(ll a,ll b)
{
return z(a*b+(1-a)*(1-b));
}
int rd()
{
int ret=0; char gc=getchar();
while(gc<'0'||gc>'9') gc=getchar();
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret;
}
int ls[maxn<<8],rs[maxn<<8],rt[maxn<<2];
ll s[maxn<<8];
void up1(int l,int r,int &x,int a,int b,ll c)
{
if(!x) x=++tot,s[x]=1;
if(a<=l&&r<=b)
{
s[x]=calc(s[x],c);
return ;
}
int mid=l+r>>1;
if(a<=mid) up1(l,mid,ls[x],a,b,c);
if(b>mid) up1(mid+1,r,rs[x],a,b,c);
}
ll q1(int l,int r,int x,int a)
{
if(!x) return 1;
if(l==r) return s[x];
int mid=l+r>>1;
if(a<=mid) return calc(s[x],q1(l,mid,ls[x],a));
else return calc(s[x],q1(mid+1,r,rs[x],a));
}
void up2(int l,int r,int x,int a,int b,int c,int d,ll e)
{
if(a<=l&&r<=b)
{
up1(1,n,rt[x],c,d,e);
return ;
}
int mid=l+r>>1;
if(a<=mid) up2(l,mid,lson,a,b,c,d,e);
if(b>mid) up2(mid+1,r,rson,a,b,c,d,e);
}
ll q2(int l,int r,int x,int a,int b)
{
if(l==r) return q1(1,n,rt[x],b);
int mid=l+r>>1;
if(a<=mid) return calc(q1(1,n,rt[x],b),q2(l,mid,lson,a,b));
else return calc(q1(1,n,rt[x],b),q2(mid+1,r,rson,a,b));
}
int main()
{
n=rd(),m=rd();
int i,a,b,c;
ll p,q;
for(i=1;i<=m;i++)
{
c=rd(),a=rd(),b=rd();
if(c==1)
{
p=inv(b-a+1);
if(a>1) up2(0,n,1,1,a-1,a,b,z(1-p)),up2(0,n,1,0,0,0,a-1,0);
if(b<n) up2(0,n,1,a,b,b+1,n,z(1-p)),up2(0,n,1,0,0,b+1,n,0);
up2(0,n,1,a,b,a,b,z(1-2*p)),up2(0,n,1,0,0,a,b,p);
}
else printf("%lld\n",q2(0,n,1,a-1,b));
}
return 0;
}
【BZOJ4785】[Zjoi2017]树状数组 树套树(二维线段树)的更多相关文章
- tyvj P1716 - 上帝造题的七分钟 二维树状数组区间查询及修改 二维线段树
P1716 - 上帝造题的七分钟 From Riatre Normal (OI)总时限:50s 内存限制:128MB 代码长度限制:64KB 背景 Background 裸体就意味着 ...
- 洛谷 P3688 - [ZJOI2017]树状数组(二维线段树+标记永久化)
题面传送门 首先学过树状数组的应该都知道,将树状数组方向写反等价于前缀和 \(\to\) 后缀和,因此题目中伪代码的区间求和实质上是 \(sum[l-1...n]-sum[r...n]=sum[l-1 ...
- 二维线段树 poj-2155
题意:t组样例 ,输入 n,m,表示n*n的矩阵进行m次操作 ,C: 输入两个坐标 ,组成的矩形 进行取反操作 ,Q:对输的坐标位置输入其值. 思路:一开始想的是用1000(表示x轴)个线段树(对每段 ...
- BZOJ4785 ZJOI2017树状数组(概率+二维线段树)
可以发现这个写挂的树状数组求的是后缀和.find(r)-find(l-1)在模2意义下实际上查询的是l-1~r-1的和,而本来要查询的是l~r的和.也就是说,若结果正确,则a[l-1]=a[r](mo ...
- [BZOJ4785][ZJOI2017]树状数组(概率+二维线段树)
4785: [Zjoi2017]树状数组 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 297 Solved: 195[Submit][Status ...
- BZOJ4785 [Zjoi2017]树状数组 【二维线段树 + 标记永久化】
题目链接 BZOJ4785 题解 肝了一个下午QAQ没写过二维线段树还是很难受 首先题目中的树状数组实际维护的是后缀和,这一点凭分析或经验或手模观察可以得出 在\(\mod 2\)意义下,我们实际求出 ...
- bzoj4785:[ZJOI2017]树状数组:二维线段树
分析: "如果你对树状数组比较熟悉,不难发现可怜求的是后缀和" 设数列为\(A\),那么可怜求的就是\(A_{l-1}\)到\(A_{r-1}\)的和(即\(l-1\)的后缀减\( ...
- 「ZJOI2017」树状数组(二维线段树)
「ZJOI2017」树状数组(二维线段树) 吉老师的题目真是难想... 代码中求的是 \(\sum_{i=l-1}^{r-1}a_i\),而实际求的是 \(\sum_{i=l}^{r}a_i\),所以 ...
- BZOJ.4553.[HEOI2016&TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)
题目链接:BZOJ 洛谷 \(O(n^2)\)DP很好写,对于当前的i从之前满足条件的j中选一个最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++j) if(a[ ...
随机推荐
- 【转载】struts应用在断网情况下启动报错解决办法(java/net/AbstractPlainSocketImpl.java:178:-1)
无意间struts应用在有网络的情况下启动正常,在断网的情况下启动报错,报错代码如下图所示: SEVERE: Exception starting filter struts2 Class: java ...
- ZeroMQ使用学习记录(转)
ZMQ简介 ZMQ(ØMQ.ZeroMQ, 0MQ)看起来像是一套嵌入式的网络链接库,但工作起来更像是一个并发式的框架.它提供的套接字可以在多种协议中传输消息,如线程间.进程间.TCP.广播等.你可以 ...
- C# HttpWebRequest 绝技 【转】
原文地址:http://www.sufeinet.com/thread-6-1-1.html 在线测试工具http://www.sufeinet.com/thread-3690-1-1.html c# ...
- [GraphQL] Query a GraphQL API with graphql-request
To query a GraphQL API, all you need to do is send an HTTP request that includes the query operation ...
- AndroidStudio短信验证功能收不到验证码
http://mob.com/第三方接口获取地址: 登陆过后点我的后台即可上传,管理应用.需注意的是,即使验证不通过,只要整合了短信验证的Jar包,每天都有20条免费验证短信.现在的mob.com只支 ...
- OpenCV2学习笔记(十五):利用Cmake高速查找OpenCV函数源代码
在使用OpenCV时,在对一个函数的调用不是非常了解的情况下,通常希望查到该函数的官方声明.而假设想进一步研究OpenCV的函数,则必须深入到源码. 在VS中我们能够选中想要查看的OpenCV函数,点 ...
- C1:工厂模式 Factory
最常用的实例化对象模式,用工厂方法替代了实例化对象. 应用场景:A.一个类的子类经常面临着剧烈变化,但却拥有较稳定的接口,或者说拥有相同的接口.工厂方法定义一个用于创建对象的接口,让子类来决定创建那个 ...
- ionic准备之angular基础——格式化数据以及过滤器(8)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Xcode强大的多视图立体分层显示View UI Herarchy
Xcode能够显示执行页面的立体uivew结构图,能够让你看到一个页面包括哪些视图,在哪一层,在页面的什么位置. 一看就能看到你的uiview是否显示.显示在哪里了. 用鼠标点击页面移动鼠标能够看到页 ...
- python 三个双引号
有的内容被上面三个双引号和下面三个双引号包围了,这些内容不执行.即,下面aaa的部分不执行. """ aaaa; """