可以发现这个写挂的树状数组求的是后缀和。find(r)-find(l-1)在模2意义下实际上查询的是l-1~r-1的和,而本来要查询的是l~r的和。也就是说,若结果正确,则a[l-1]=a[r](mod 2)。

  一个很容易想到的思路是线段树维护每一位为1的概率。然而这其实是不对的,因为每一位是否为1并非独立事件。

  世界上没有什么事情是用一维线段树解决不了的,如果有,那就两维

  我们维护每两位之间相同的概率。考虑一次操作对某两位的影响。若该次操作包含两位中的x位,那么改变两者间相同状态的概率就是x/len,len为该次修改的区间长度。设原相同概率为pi,j,那么操作后概率就变为(1-pi,j)*x/len+pi,j*(1-x/len)。这个式子神奇的满足交换律结合律,算的时候我们就不用管顺序了。

  用二维线段树就可以维护了。修改时先拆成外层线段树上的区间再在内层线段树修改,查询时将所有外层区间包含该点的内层线段树上的操作合在一起。并且需要标记永久化。

  注意l=1时find(l-1)直接返回0而不是整个数组的和,此时询问的是r后缀和r前缀是否相等,需要特判一下,记录修改了几次以及r这一位为0的概率(即和第0位相同的概率)。

  (写起来出乎意料的短

  (空间需要开的非常大

  (当然也可以cdq分治变成静态数点扫描线扫过去就好了

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 100010
#define P 998244353
int n,m,tot=,inv[N],L[N<<],R[N<<],cnt=;
int root[N<<];
struct data{int l,r,x;
}tree[N*];
void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
int calc(int x,int y){return (1ll*x*(P+-y)+1ll*y*(P+-x))%P;}
void build(int k,int l,int r)
{
L[k]=l,R[k]=r;
if (l==r) return;
int mid=l+r>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
}
void modify(int &k,int l,int r,int x,int y,int a)
{
if (!k) k=++cnt;
if (l==x&&r==y)
{
tree[k].x=calc(tree[k].x,a);
return;
}
int mid=l+r>>;
if (y<=mid) modify(tree[k].l,l,mid,x,y,a);
else if (x>mid) modify(tree[k].r,mid+,r,x,y,a);
else modify(tree[k].l,l,mid,x,mid,a),modify(tree[k].r,mid+,r,mid+,y,a);
}
int query(int k,int l,int r,int x,int ans)
{
if (!k) return ans;
ans=calc(ans,tree[k].x);
if (l==r) return ans;
int mid=l+r>>;
if (x<=mid) return query(tree[k].l,l,mid,x,ans);
else return query(tree[k].r,mid+,r,x,ans);
}
void change(int k,int l,int r,int x,int y,int a)
{
if (L[k]==l&&R[k]==r) {modify(root[k],,n,x,y,a);return;}
int mid=L[k]+R[k]>>;
if (r<=mid) change(k<<,l,r,x,y,a);
else if (l>mid) change(k<<|,l,r,x,y,a);
else change(k<<,l,mid,x,y,a),change(k<<|,mid+,r,x,y,a);
}
int getans(int x,int y)
{
int k=,s=;
while ()
{
s=calc(s,query(root[k],,n,y,));
if (L[k]==R[k]) break;
if (x<=(L[k]+R[k]>>)) k<<=;
else k=k<<|;
}
return s%P;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj4785.in","r",stdin);
freopen("bzoj4785.out","w",stdout);
const char LL[]="%I64d";
#else
const char LL[]="%lld";
#endif
n=read(),m=read();
inv[]=;
for (int i=;i<=n;i++) inv[i]=(P-1ll*(P/i)*inv[P%i]%P)%P;
build(,,n);
for (int i=;i<=m;i++)
{
int op=read(),l=read(),r=read();
if (op==)
{
tot^=;int x=inv[r-l+];
change(,,l-,l,r,x);
if (r<n) change(,l,r,r+,n,x);
change(,l,r,l,r,x*%P);
}
else
{
if (l==) printf("%d\n",tot?(P+-getans(l-,r))%P:getans(l-,r));
else printf("%d\n",getans(l-,r));
}
}
return ;
}

BZOJ4785 ZJOI2017树状数组(概率+二维线段树)的更多相关文章

  1. [BZOJ4785][ZJOI2017]树状数组(概率+二维线段树)

    4785: [Zjoi2017]树状数组 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 297  Solved: 195[Submit][Status ...

  2. 「ZJOI2017」树状数组(二维线段树)

    「ZJOI2017」树状数组(二维线段树) 吉老师的题目真是难想... 代码中求的是 \(\sum_{i=l-1}^{r-1}a_i\),而实际求的是 \(\sum_{i=l}^{r}a_i\),所以 ...

  3. BZOJ4785 [Zjoi2017]树状数组 【二维线段树 + 标记永久化】

    题目链接 BZOJ4785 题解 肝了一个下午QAQ没写过二维线段树还是很难受 首先题目中的树状数组实际维护的是后缀和,这一点凭分析或经验或手模观察可以得出 在\(\mod 2\)意义下,我们实际求出 ...

  4. P3688 [ZJOI2017] 树状数组 【二维线段树】

    题目描述:这里有一个写挂的树状数组: 有两种共\(m\)个操作: 输入\(l,r\),在\([l,r]\)中随机选择一个整数\(x\)执行\(\text{Add}(x)\) 输入\(l,r\),询问执 ...

  5. 洛谷 P3688 - [ZJOI2017]树状数组(二维线段树+标记永久化)

    题面传送门 首先学过树状数组的应该都知道,将树状数组方向写反等价于前缀和 \(\to\) 后缀和,因此题目中伪代码的区间求和实质上是 \(sum[l-1...n]-sum[r...n]=sum[l-1 ...

  6. luogu3380/bzoj3196 二逼平衡树 (树状数组套权值线段树)

    带修改区间K大值 这题有很多做法,我的做法是树状数组套权值线段树,修改查询的时候都是按着树状数组的规则找出那log(n)个线段树根,然后一起往下做 时空都是$O(nlog^2n)$的(如果离散化了的话 ...

  7. CF1093E Intersection of Permutations 树状数组套权值线段树

    \(\color{#0066ff}{ 题目描述 }\) 给定整数 \(n\) 和两个 \(1,\dots,n\) 的排列 \(a,b\). \(m\) 个操作,操作有两种: \(1\ l_a\ r_a ...

  8. BZOJ2141排队——树状数组套权值线段树(带修改的主席树)

    题目描述 排排坐,吃果果,生果甜嗦嗦,大家笑呵呵.你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家 乐和和.红星幼儿园的小朋友们排起了长长地队伍,准备吃果果.不过因为小朋友们的身高有所区别 ...

  9. Dynamic Rankings(树状数组套权值线段树)

    Dynamic Rankings(树状数组套权值线段树) 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[ ...

  10. [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)

    [BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ...

随机推荐

  1. lesson3:小程序

    问题: 一·设计思想 创建一个静态变量,利用构造函数在每次创建对象时运行的机制,计算创建对象个数. 二·程序流程图 三·程序源代码 public class Test9{ public static ...

  2. oa tomcat 代码处理跨域问题

    meta标签处理http.https跨域 <!-- 将http请求转成https请求 --> <meta http-equiv="Content-Security-Poli ...

  3. 将WinForm程序(含多个非托管Dll)合并成一个exe的方法

    原文:将WinForm程序(含多个非托管Dll)合并成一个exe的方法 开发程序的时候经常会引用一些第三方的DLL,然后编译生成的exe文件就不能脱离这些DLL独立运行了. ILMerge能把托管dl ...

  4. 汇编 SETG,SETL ,SETGE, SETLE指令

    一.SETG SETZ(SETE) //取ZF标志位值 放到寄存器里 SETNZ(SETNE) == > SETG //setg cl//ZF==0 并 SF==0 并 OF==0 时 cl=1 ...

  5. Postgres使用ALTER USER命令修改用户的密码、密码过期,锁定,解锁

    使用ALTER USER命令修改用户的密码.密码过期,锁定,解锁 (1)修改用户的口令,将用户的口令修改为新的密码 highgo=#create user test with password ‘te ...

  6. c++对象模型-对象模型

    1:简单对象模型 1>介绍:每个成员都使用一个指针指向真正的成员.所以对象 的大小很好确定,就是成员数*指针大小. 2>用途:成员函数就是使用这个模型的 3>图: 4>加上继承 ...

  7. 在线排错之curl命令详解

    春回大地万物复苏,好久不来,向各位博友问好. 简介 cURL是一个利用URL语法在命令行下工作的文件传输工具,1997年首次发行.它支持文件上传和下载,所以是综合传输工具,但按传统,习惯称cURL为下 ...

  8. C#大型电商项目优化(二)——嫌弃EF与抛弃EF

    上一篇博文中讲述了使用EF开发电商项目的代码基础篇,提到EF后,一语激起千层浪.不少园友纷纷表示:EF不适合增长速度飞快的互联网项目,EF只适合企业级应用等等. 也有部分高手提到了分布式,确实,性能优 ...

  9. KVM虚拟机管理——虚拟机克隆

    1. 概述2. 部署基本操作系统虚拟机3. 配置虚拟机3.1 修改/etc/sysconfig/network3.2 删除/etc/sysconfig/network-scripts/ifcfg-et ...

  10. PMO在组织中实现价值应做的工作

    PMO在组织中实现价值应做的工作 研发人员及项目经理常常对PMO有反感情绪,认为其不熟悉业务流程与技术.经常要求项目经理和研发人员提交形式化的材料,只审批和监控,不能为项目提供良好的服务.在很多企业, ...