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

4785: [Zjoi2017]树状数组

Time Limit: 40 Sec  Memory Limit: 512 MB
Submit: 297  Solved: 195
[Submit][Status][Discuss]

Description

漆黑的晚上,九条可怜躺在床上辗转反侧。难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历。那是一道

基础的树状数组题。给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种:

1 x,表示将 Ax 变成 (Ax + 1) mod 2。

2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r

尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做。当时非常young 的她写了如下的算

法:

1: function Add(x)

2: while x > 0 do

3: A

x ← (Ax + 1) mod 2

4: x ← x ? lowbit(x)

5: end while

6: end function

7:

8: function Find(x)

9: if x == 0 then

10: return 0

11: end if

12: ans ← 0

13: while x ≤ n do

14: ans ← (ans + Ax) mod 2

15: x ← x + lowbit(x)

16: end while

17: return ans

18: end function

19:

20: function Query(l, r)

21: ansl ← Find(l ? 1)

22: ansr ← Find(r)

23: return (ansr ? ansl + 2) mod 2

24: end function

其中 lowbit(x) 表示数字 x 最?的非 0 二进制位,例如 lowbit(5) = 1, lowbit(12) = 4。进行第一类操作的时

候就调用 Add(x),第二类操作的时候答案就是 Query(l, r)。如果你对树状数组比较熟悉,不难发现可怜把树状

数组写错了: Add和Find 中 x 变化的方向反了。因此这个程序在最终测试时华丽的爆 0 了。然而奇怪的是,在

当时,这个程序通过了出题人给出的大样例——这也是可怜没有进行对拍的原因。现在,可怜想要算一下,这个程

序回答对每一个询问的概率是多少,这样她就可以再次的感受到自己是一个多么非的人了。然而时间已经过去了很

多年,即使是可怜也没有办法完全回忆起当时的大样例。幸运的是,她回忆起了大部分内容,唯一遗忘的是每一次

第一类操作的 x的值,因此她假定这次操作的 x 是在 [li, ri] 范围内 等概率随机 的。具体来说,可怜给出了

一个长度为 n 的数组 A,初始为 0,接下来进行了 m 次操作:

1 l r,表示在区间 [l, r] 中等概率选取一个 x 并执行 Add(x)。

2 l r,表示询问执行 Query(l, r) 得到的结果是正确的概率是多少。

Input

第一行输入两个整数 n, m。

接下来 m 行每行描述一个操作,格式如题目中所示。

N<=10^5,m<=10^5,1<=L<=R<=N

Output

对于每组询问,输出一个整数表示答案。如果答案化为最简分数后形如 x/y

,那么你只需要输出 x*y^?1 mod 998244353 后的值。(即输出答案模 998244353)。

Sample Input

5 5

1 3 3

2 3 5

2 4 5

1 1 3

2 2 5

Sample Output

1

0

665496236

//在进行完 Add(3) 之后, A 数组变成了 [0, 1, 1, 0, 0]。所以前两次询问可怜的程序答案都是

1,因此第一次询问可怜一定正确,第二次询问可怜一定错误。

首先经过分析证明可得,树状数组只是一层外衣,实际上题目就是求[l-1,r-1]和[l,r]的改变次数的差为偶数的概率,也就是l-1和r改变次数差为偶数的概率。(l==1的情况要特殊处理,也就是[1,r-1]和[r+1,n]的总改变次数差为偶数的概率)

想到这里之后,我们会有一个看似正确的直觉:可以通过动规+前缀和求出每个数被改变奇数次和偶数次的概率。但是实际上由于动规方程里的并不是互斥事件,所以概率不可以直接相乘。

所以我们可以肯定,一定是对每个数,依次遍历所有的修改,已修改时间为下标做DP。这样就有了一个简单的50分做法。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #define rep(i,l,r) for (int i=l; i<=r; i++)
 6 typedef long long ll;
 7 using namespace std;
 8
 9 const int N=3005,md=998244353;
10 int dp[N],n,m,op,l,r,cnt;
11 struct node{ int l,r,v; }G[N];
12
13 int ksm(int x,int y){
14     int res=1;
15     for(; y; y>>=1,x=(ll)x*x%md)
16         if (y&1) res=(ll)res*x%md;
17     return res;
18 }
19
20 int main(){
21     freopen("bit.in","r",stdin);
22     freopen("bit.out","w",stdout);
23     scanf("%d%d",&n,&m);
24     while (m--){
25         scanf("%d%d%d",&op,&l,&r);
26         if (op==1) G[cnt++]=(node){l,r,ksm(r-l+1,md-2)};
27         else{
28             int ans=1;
29             if(l==1){
30                 rep(i,0,cnt)
31                     if(G[i].r>=l){
32                         int len=(G[i].r-max(l,G[i].l)+1-(G[i].r>=r&&G[i].l<=r));
33                         len = len*(ll)G[i].v%md;
34                         ans=(((ll)ans*(1-len)+(ll)(1-ans)*len)%md+md)%md;
35                     }
36             }else
37                 rep(i,0,cnt)
38                     if(G[i].l<=l-1&&G[i].r>=r){
39                         int len=G[i].v*(ll)2%md;
40                         ans=(((ll)ans*(1-len)+(ll)(1-ans)*len)%md+md)%md;
41                     }else if((G[i].l<=l-1&&G[i].r>=l-1)||(G[i].l<=r&&G[i].r>=r)){
42                         int len=G[i].v;
43                         ans=(((ll)ans*(1-len)+(ll)(1-ans)*len)%md+md)%md;
44                     }
45             printf("%d\n",ans);
46         }
47     }
48     return 0;
49 }

那么如果想到了这里,已经不难进一步想出用数据结构来维护了。将每个询问映射成二维平面上的点(l-1,r),(类似BZOJ4826影魔),然后用二维线段树实现矩形区间修改,l==1的情况只要一维线段树即可。

UOJ上可能有BUG,在线自定义测试的时候程序正常运行并返回正确结果,同样的数据提交评测却无限RE。能过官方数据,BZOJ上AC,UOJ上的HACK数据没有测。

现在总结一下写代码的时候需要注意的地方。

1.每写完一段停下来检查一下

2.修改程序的时候要慢一点,检查修改是否正确并思考是否有类似地方需要修改。

3.调试时多想想平时遇到的问题优先考虑。

4.多总结遇到的错误(特别是模板方面),以1A为最终目标。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define lc (x<<1)
 4 #define rc ((x<<1)|1)
 5 #define rep(i,l,r) for (int i=l; i<=r; i++)
 6 using namespace std;
 7
 8 const int N=200100,md=998244353;
 9 int n,m,cnt,nd,op,l,r,rt[N*3],P[N*180];
10 struct D{ int ls,rs; }a[N*180];
11 int F(int x,int y){ return (1ll*x*y%md+1ll*(1-x+md)*(1-y+md)%md)%md; }
12
13 int ksm(int a,int b){
14     int res;
15     for (res=1; b; a=(1ll*a*a)%md,b>>=1)
16         if (b & 1) res=(1ll*res*a)%md;
17     return res;
18 }
19
20 void mdf(int &x,int L,int R,int l,int r,int k){
21     if (!x) x=++nd,P[x]=1;
22     if (L==l && r==R) { P[x]=F(P[x],k); return; }
23     int mid=(L+R)>>1;
24     if (r<=mid) mdf(a[x].ls,L,mid,l,r,k);
25     else if (l>mid) mdf(a[x].rs,mid+1,R,l,r,k);
26         else mdf(a[x].ls,L,mid,l,mid,k),mdf(a[x].rs,mid+1,R,mid+1,r,k);
27 }
28
29 int que(int x,int L,int R,int pos){
30     if (!x) return 1;
31     if (L==R) return P[x]; int mid=(L+R)>>1;
32     if (pos<=mid) return F(P[x],que(a[x].ls,L,mid,pos));
33         else return F(P[x],que(a[x].rs,mid+1,R,pos));
34 }
35
36 void Mdf(int x,int L,int R,int l,int r,int xx,int yy,int k){
37     if (L==l && r==R){ mdf(rt[x],0,n+1,xx,yy,k); return; }
38     int mid=(L+R)>>1;
39     if (r<=mid) Mdf(lc,L,mid,l,r,xx,yy,k);
40     else if (l>mid) Mdf(rc,mid+1,R,l,r,xx,yy,k);
41         else Mdf(lc,L,mid,l,mid,xx,yy,k),Mdf(rc,mid+1,R,mid+1,r,xx,yy,k);
42 }
43
44 int Que(int x,int L,int R,int posx,int posy){
45     int res=1;
46     if (rt[x]) res=F(res,que(rt[x],0,n+1,posy));
47     if (L==R) return res; int mid=(L+R)>>1;
48     if (posx<=mid) res=F(res,Que(lc,L,mid,posx,posy));
49         else res=F(res,Que(rc,mid+1,R,posx,posy));
50     return res;
51 }
52
53 int main(){
54     freopen("bit.in","r",stdin);
55     freopen("bit.out","w",stdout);
56     scanf("%d%d",&n,&m);
57     rep(i,1,m){
58         scanf("%d%d%d",&op,&l,&r);
59         if (op==1){
60             int p=ksm(r-l+1,md-2);
61             if (l>1) Mdf(1,1,n,1,l-1,l,r,(1-p+md)%md),mdf(rt[0],1,n,1,l-1,0);
62             if (r<n) Mdf(1,1,n,l,r,r+1,n,(1-p+md)%md),mdf(rt[0],1,n,r+1,n,0);
63             if (l!=r) Mdf(1,1,n,l,r,l,r,(1-p*2+md+md)%md);
64             mdf(rt[0],1,n,l,r,p);
65         }else{
66             if (l==1) printf("%d\n",que(rt[0],1,n,r)); else printf("%d\n",Que(1,1,n,l-1,r));
67         }
68     }
69     return 0;
70 }

原文地址:https://www.cnblogs.com/HocRiser/p/8543389.html

时间: 2024-10-26 17:27:28

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

poj 1195 二维树状数组 及二维树状数组模板

http://poj.org/problem?id=1195 求矩阵和的时候,下标弄错WA了一次... 求矩形(x1,y1) (x2,y2)的sum |sum=sum(x2,y2)-sum(x1-1,y2)-sum(x2,y1-1)+sum(x1-1,y1-1) 二维树状数组讲解:http://blog.csdn.net/u011026968/article/details/38532117 二维树状数组模板: /*========================================

POJ2352 Stars 【树状数组】or【线段树】

Stars Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 31172   Accepted: 13595 Description Astronomers often examine star maps where stars are represented by points on a plane and each star has Cartesian coordinates. Let the level of a st

实用数据结构---树状数组(二叉索引树)

树状数组适用于动态连续和查询问题,就是给定一个区间, 查询某一段的和或者修改某一位置的值. 关于树状数组的结构请去百度百科,否则将看不懂下面内容 我们看这个题 士兵杀敌(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:5 描述 南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的. 小工是南将军手下的军师,南将军经常想知道第m号到第n号士兵的总杀敌数,请你帮助小工来回答南将军吧. 南将军的某次询问之后士兵i可能又杀敌q人,之后南将军再询问的时候,需要考虑到新

浅析树状数组(二叉索引树)及一些模板

树状数组 动态连续和查询问题.给定一个n个元素的数组a1.a2.……,an,设计一个数据结构,支持以下两种操作:1.add(x,d):让ax增加d;2.query(l,r):计算al+al+1+…+ar 如何让query和add都能快速完成呢?方法有很多,这里介绍的便是树状数组.为此我们先介绍lowbit. 对于正整数x,我们定义lowbit(x)为x的二进制表达式中最右边的1所对应的值(而不是这个比特的序号).比如,38288的二进制1001010110010000,所以lowbit(3828

【树状数组(二叉索引树)】轻院热身—candy、NYOJ-116士兵杀敌(二)

[概念] 转载连接:树状数组 讲的挺好. 这两题非常的相似,查询区间的累加和.更新结点.Add(x,d) 与 Query(L,R) 的操作 [题目链接:candy] 唉,也是现在才发现这题用了这个知识,当初A的第一个数据结构的题就是关于树状数组的,忘了忘了.. Problem C: candy Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 252  Solved: 63 SubmitStatusWeb Board Description Kimi

树状数组(二叉索引树 BIT Fenwick树) *【一维基础模板】(查询区间和+修改更新)

刘汝佳:<训练指南>Page(194) #include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> using namespace std; //一维树状数组基础模板 int lowbit(int x) { return x&(-x); } int c[1001]; int sum(int x) //计算从1到x的数组元素的和 { int

LightOJ 1085(树状数组+离散化+DP,线段树)

All Possible Increasing Subsequences Time Limit:3000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Appoint description: Description An increasing subsequence from a sequence A1, A2 ... An is defined by Ai1, Ai2 ... Aik, where the followi

【树状数组套权值线段树】bzoj1901 Zju2112 Dynamic Rankings

谁再管这玩意叫树状数组套主席树我跟谁急 明明就是树状数组的每个结点维护一棵动态开结点的权值线段树而已 好吧,其实只有一个指针,指向该结点的权值线段树的当前结点 每次查询之前,要让指针指向根结点 不同结点的权值线段树之间毫无关联 可以看这个:http://blog.csdn.net/popoqqq/article/details/40108669?utm_source=tuicool #include<cstdio> #include<algorithm> using namespa

【树状数组】树状数组一维二维模板

以下模板都是点更新,区间查询,如果是区间更新点查询,只需将利用lowbit的循环方向倒过来 一维: inline int lowbit(int x) { return x & -x; } void add(int x, int val) { for(int i = x; i <= n; i += lowbit(i)) C[i] += val; } int sum(int x) { int ret = 0; for(int i = x; i > 0; i -= lowbit(i)) re