bzoj4785:[ZJOI2017]树状数组:二维线段树
分析:
“如果你对树状数组比较熟悉,不难发现可怜求的是后缀和”
设数列为\(A\),那么可怜求的就是\(A_{l-1}\)到\(A_{r-1}\)的和(即\(l-1\)的后缀减\(r\)的后缀,\(\sum_{i=l-1}^{r-1}A_i\)),而答案为\(A_l\)到\(A_r\)的和(即\(\sum_{i=l}^{r}A_i\))这两种答案都包含\(A_l\)到\(A_{r-1}\)的和,因此只需判断\(A_{l-1}\)与\(A_r\)相等的概率就行了
那么怎么算?
考虑记下每次修改的影响,假设已知左端点\(a\)和右端点\(b\),那么对于某一次修改区间\(l\)~\(r\),则只有当\(a\in[l,r]\)或\(b\in[l,r]\)时才有影响,设\(p\)为任选区间内一个数的概率,这里分三种情况讨论:
- \(a\in[1,l-1]\),\(b\in[l,r]\)时,有\(1-p\)的概率不影响
- \(a\in[l,r]\),\(b\in[l,r]\)时,有\(1-2*p\)的概率不影响
- \(a\in[l,r]\),\(b\in[r+1,n]\)时,有\(1-p\)的概率不影响
那么只要把所有的影响都合并起来就行了,设当前相同概率为\(p\),当前修改不影响的概率\(q\),则相同概率更新为\(p*q+(1-p)*(1-q)\)
但是直接朴素必然TLE,因此我们要寻找更高效的算法
考虑二维线段树,设点\((x,y)\)表示\(A_x\)与\(A_y\)相等的概率,那么我们会惊奇的发现:
这不就是区间修改单点查询吗!
每读入一个修改,就用上面所说的影响更新区间,即\([1,l-1,l,r],[l,r,l,r],[l,r,r+1,n]\)三个区间,用上述式子合并区间
询问即查询点\((l-1,r)\)的值
还有一个坑点!!\(l\)可能为\(1\)!!
\(l=1\)时,可怜求的是\(r\)的后缀和,因此我们需要求\(r\)的后缀和与前缀和相等的概率
这也可以用类似方法,第一维我们新增一个元素\(0\),用\([0,x]\)表示\(x\)的后缀和与前缀和相等的概率,那么当修改区间\([l,r]\)时,区间\([1,l-1]\),\([r+1,n]\)中元素的后缀和与前缀和一定会被影响,即不被影响概率为\(0\);而区间\([l,r]\)中元素有\(p\)的概率不被影响(即正好选到它,\(p\)的意义即为上述),这时我们也要更新。这样当\(l=1\)时,直接查询点\((l-1,r)\)的值即可
还有就是卡卡常数,卡卡空间
以及线段树要动态开点
Code:
(代码丑不要怪我)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<iostream>
#include<queue>
#include<iomanip>
#include<algorithm>
using namespace std;
const int N=100010;
const int MOD=998244353;
int rt[N*21],n,cnt;
struct tree
{
int l,r;
int v;//卡空间,开int
}tr[N*402];
inline long long mul(long long p,long long q)//p*q+(1-p)*(1-q)
{
long long res=p*q%MOD;
res=(res+(1-p+MOD)*(1-q+MOD)%MOD)%MOD;
return res;
}
inline long long power(long long x,long long y)//快速幂
{
long long ans=1;
while(y)
{
if(y&1) ans=ans*x%MOD;
x=x*x%MOD,y>>=1;
}
return ans;
}
inline void updatay(int l,int r,int &id,int ly,int ry,long long p)//修改区间二维
{
if(id==0)
{
cnt++;
id=cnt;
tr[id].v=1;//初始时都是0,因此相等概率为1
}
if(l>=ly&&r<=ry)
{
tr[id].v=mul(p,tr[id].v);
return;
}
int mid=l+r>>1;
if(ly<=mid) updatay(l,mid,tr[id].l,ly,ry,p);
if(ry>mid) updatay(mid+1,r,tr[id].r,ly,ry,p);
}
inline void updatax(int l,int r,int id,int lx,int rx,int ly,int ry,long long p)//修改区间一维
{
if(l>=lx&&r<=rx)
{
updatay(1,n,rt[id],ly,ry,p);
return;
}
int mid=l+r>>1;
if(lx<=mid) updatax(l,mid,id<<1,lx,rx,ly,ry,p);
if(rx>mid) updatax(mid+1,r,id<<1|1,lx,rx,ly,ry,p);
}
long long quey(int l,int r,int id,int y)//查询二维
{
if(id==0) return 1;//初始时都是0,因此相等概率为1
if(l==r) return tr[id].v;
int mid=l+r>>1;
long long res;
if(y<=mid) res=mul(tr[id].v,quey(l,mid,tr[id].l,y));
else res=mul(tr[id].v,quey(mid+1,r,tr[id].r,y));
//合并沿途所有区间影响值
return res;
}
long long quex(int l,int r,int id,int x,int y)//查询一维
{
if(l==r) return quey(1,n,rt[id],y);
int mid=l+r>>1;
if(x<=mid) return mul(quey(1,n,rt[id],y),quex(l,mid,id<<1,x,y));
else return mul(quey(1,n,rt[id],y),quex(mid+1,r,id<<1|1,x,y));
//合并沿途所有区间影响值
}
int main()
{
int i,j,k,q,op,l,r;
long long p;
scanf("%d%d",&n,&q);
cnt=0;
while(q--)
{
scanf("%d%d%d",&op,&l,&r);
if(op==1)
{
p=power(r-l+1,MOD-2);//求逆元,即选某一个元素的概率
if(l>1) updatax(0,n,1,1,l-1,l,r,(1-p+MOD)%MOD),updatax(0,n,1,0,0,1,l-1,0);
if(r<n) updatax(0,n,1,l,r,r+1,n,(1-p+MOD)%MOD),updatax(0,n,1,0,0,r+1,n,0);
updatax(0,n,1,l,r,l,r,(1-p*2%MOD+MOD)%MOD),updatax(0,n,1,0,0,l,r,p);
}
else printf("%lld\n",quex(0,n,1,l-1,r));
}
return 0;
}
bzoj4785:[ZJOI2017]树状数组:二维线段树的更多相关文章
- BZOJ 4785 [Zjoi2017]树状数组 | 二维线段树
题目链接 BZOJ 4785 题解 这道题真是令人头秃 = = 可以看出题面中的九条可怜把求前缀和写成了求后缀和,然后他求的区间和却仍然是sum[r] ^ sum[l - 1],实际上求的是闭区间[l ...
- BZOJ4822[Cqoi2017]老C的任务——树状数组(二维数点)
题目描述 老 C 是个程序员. 最近老 C 从老板那里接到了一个任务——给城市中的手机基站写个管理系统.作为经验丰富的程序员,老 C 轻松 地完成了系统的大部分功能,并把其中一个功能交给你来实 ...
- BZOJ1935: [Shoi2007]Tree 园丁的烦恼(树状数组 二维数点)
题意 题目链接 Sol 二维数点板子题 首先把询问拆成四个矩形 然后离散化+树状数组统计就可以了 // luogu-judger-enable-o2 #include<bits/stdc++.h ...
- 树状数组 二维偏序【洛谷P3431】 [POI2005]AUT-The Bus
P3431 [POI2005]AUT-The Bus Byte City 的街道形成了一个标准的棋盘网络 – 他们要么是北南走向要么就是西东走向. 北南走向的路口从 1 到 n编号, 西东走向的路从1 ...
- 树状数组+二维前缀和(A.The beautiful values of the palace)--The Preliminary Contest for ICPC Asia Nanjing 2019
题意: 给你螺旋型的矩阵,告诉你那几个点有值,问你某一个矩阵区间的和是多少. 思路: 以后记住:二维前缀和sort+树状数组就行了!!!. #define IOS ios_base::sync_wit ...
- bzoj 4822: [Cqoi2017]老C的任务【扫描线+树状数组+二维差分】
一个树状数组能解决的问题分要用树套树--还写错了我别是个傻子吧? 这种题还是挺多的,大概就是把每个矩形询问差分拆成四个点前缀和相加的形式(x1-1,y1-1,1)(x2.y2,1)(x1-1,y2,- ...
- 【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询
Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位 ...
- [Usaco2014 Open Gold ]Cow Optics (树状数组+扫描线/函数式线段树)
这道题一上手就知道怎么做了= = 直接求出原光路和从目标点出发的光路,求这些光路的交点就行了 然后用树状数组+扫描线或函数式线段树就能过了= = 大量的离散+模拟+二分什么的特别恶心,考试的时候是想到 ...
- HDU - 1166 树状数组模板(线段树也写了一遍)
题意: 汉语题就不说题意了,用到单点修改和区间查询(树状数组和线段树都可以) 思路: 树状数组的单点查询,单点修改和区间查询. 树状数组是巧妙运用二进制的规律建树,建树就相当于单点修改.这里面用到一个 ...
随机推荐
- GlideDemo【Glide3.7.0版本的简单使用以及圆角功能】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 本Demo主要记录Glide3.7.0版本的简单运用和实现圆角方案. 效果图 代码分析 Glide的centerCrop()和fit ...
- #3 Python面向对象(二)
前言 上一节主要记录面向对象编程的思想以及Python类的简单创建,这节继续深入类中变量的相关知识,Here we go! Python中类的各种变量 1.1 类变量 类变量定义:在类中,在函数体(方 ...
- Windows 10 安装ElasticSearch(2)- MSI安装ElasticSearch和安装Kibana
翻阅上篇文章:Windows 10 安装 ElasticSearch 上次写的是下载Zip包安装的,在下载页面 发现有 MSI (BETA) 的下载可选项.了解之后发现MSI安装也值得尝试. MSI安 ...
- 【转载】Sqlserver日期时间格式化总结
在Sqlserver数据库中,允许存储datetime的时间类型,该存储类型包含时间的时分秒以及毫秒等数值,在SQL语句查询的时候,很多时候我们需要对查询出来的日期数据进行格式化操作,Sqlserve ...
- Tkinter小技巧:如何为窗口右上角的‘x’添加一个自定义的响应函数
不废话,直接上代码 import tkinter as tk from tkinter import messagebox main_window = tk.Tk() main_window.geom ...
- PMM Client 安装异常报错
1.PMM架构 如下图所示 2.Client主要组件 PMM Client是安装在你要监视的MySQL或MongoDB主机上的一组代理组件.组件收集关于一般系统和数据库性能的各种数据,并将该数据发送到 ...
- 【Oracle教程资源大合集】Oracle数据库免费学习资源汇总
Oracle的产品非常丰富,各类学习资源也五花八门,本文将介绍Oracle官方的免费教程与风哥整理的Oracle视频教程: 1.Oracle帮助中心 Oracle帮助中心也称为Oracle文档中心,这 ...
- 位运算 leecode.389. 找不同
//给定两个字符串 s 和 t,它们只包含小写字母. //字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母. //请找出在 t 中被添加的字母 char findTheDifferenc ...
- 从0开始的Python学习012数据结构&对象与类
简介 数据结构是处理数据的结构,或者说,他们是用来存储一组相关数据的. 在Python中三种内建的数据结构--列表.元组和字典.学会了使用它们会使编程变得的简单. 列表 list是处理一组有序的数据结 ...
- MySQL8.0 on Windows下重置root密码的BUG
很多人都知道MySQL忘记root密码之后可以通过skip-grant-tables来暂时免密登录MySQL,从而修改root密码,但是这种方式一方面有安全隐患,另一方面也并不怎么适用于Windows ...