bzoj4785【Zjoi2017】树状数组

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

给出一个长度为 nn 的数组 AA,初始值都为 00,接下来进行 mm 次操作,操作有两种:

  • 1 x1 x, 表示将 AxAx 变成 (Ax+1)mod2(Ax+1)mod2。
  • 2 l r2 l r, 表示询问 (∑ri=lAi)mod2(∑i=lrAi)mod2。

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



其中 lowbit(x)lowbit(x) 表示数字 xx 最低的非 00 二进制位,例如 lowbit(5)=1,lowbit(12)=4lowbit(5)=1,lowbit(12)=4。进行第一类操作的时候就调用 Add(x)Add(x),第二类操作的时候答案就是 Query(l,r)Query(l,r)。

如果你对树状数组比较熟悉,不难发现可怜把树状数组写错了:AddAdd 和 FindFind 中 xx 变化的方向反了。因此这个程序在最终测试时华丽的爆 00 了。

然而奇怪的是,在当时,这个程序通过了出题人给出的大样例——这也是可怜没有进行对拍的原因。

现在,可怜想要算一下,这个程序回答对每一个询问的概率是多少,这样她就可以再次的感受到自己是一个多么非的人了。然而时间已经过去了很多年,即使是可怜也没有办法完全回忆起当时的大样例。幸运的是,她回忆起了大部分内容,唯一遗忘的是每一次第一类操作的 xx 的值,因此她假定这次操作的 xx 是在 [li,ri][li,ri] 范围内等概率随机的。

具体来说,可怜给出了一个长度为 nn 的数组 AA,初始为 00,接下来进行了 mm 次操作:

  • 1 l r1 l r, 表示在区间 [l,r][l,r] 中等概率选取一个 xx 并执行 Add(x)Add(x)。
  • 2 l r2 l r, 表示询问执行 Query(l,r)Query(l,r) 得到的结果是正确的概率是多少。

输入格式

第一行输入两个整数 n,mn,m。 接下来 mm 行每行描述一个操作,格式如题目中所示。


输出格式

对于每组询问,输出一个整数表示答案。如果答案化为最简分数后形如 xyxy,那么你只需要输出 x×y−1mod998244353x×y−1mod998244353 后的值。(即输出答案模 998244353998244353)。


  • 题解

    • 把询问看成修改,修改看成询问,可以发现这样子的树状数组在$l==0$时得到$0$,$l!=0$时得到l的后缀和
    • 所以问题变成了区间等概率修改+:
    • $l==1$,询问$r$的前缀和和后缀和相同的概率;
    • $l!=1$,询问$l-1$和$r$相同的概率;
    • (由于不太了解线性独立这个概念,所以试了一些很奇怪的方法例如维护相邻概率合并之类的。。。。。)
    • 考虑$[l,r]$中$l-1!=0$,等于零类似;
    • $p[x,y]$表示$x$和$y$相同的概率,由于是异或操作,只有相等和不等两种情况;
    • 对一个区间修改分一个和两个点都在区间讨论可以知道被修改的概率$q$;
    • $p = p*(1-q) + (1-p)*q$
    • 和更新顺序无关并且可以合并;
    • 二维线段树维护概率;
  •  1 #include<bits/stdc++.h>
     2 #define rg register
     3 #define ll long long
     4 using namespace std;
     5 const int N=100010,mod=998244353;
     6 int n,m,sz1,sz2,Ls[N*400],Rs[N*400],lz[N*400],rt[N<<2],ls[N<<2],rs[N<<2],Rt,ans;
     7 int inv(ll x){
     8     ll re=1;
     9     for(int y=mod-2;y;y>>=1,x=x*x%mod){
    10         if(y&1)re=re*x%mod;
    11     }
    12     return re;
    13 }
    14 char gc(){
    15     static char*p1,*p2,s[1000000];
    16     if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
    17     return(p1==p2)?EOF:*p1++;
    18 }
    19 int rd(){
    20     int x=0; char c=gc();
    21     while(c<‘0‘||c>‘9‘)c=gc();
    22     while(c>=‘0‘&&c<=‘9‘)x=x*10+c-‘0‘,c=gc();
    23     return x;
    24 }
    25 void update(int&k,int l,int r,int x,int y,int p){
    26     if(!k)lz[k=++sz2]=1;
    27     if(l==x&&r==y){
    28         lz[k]=((ll)lz[k]*(mod+1-p)+(ll)(mod+1-lz[k])*p)%mod;
    29         return ;
    30     }
    31     else{
    32         int mid=(l+r)>>1;
    33         if(y<=mid)update(Ls[k],l,mid,x,y,p);
    34         else if(x>mid)update(Rs[k],mid+1,r,x,y,p);
    35         else update(Ls[k],l,mid,x,mid,p),update(Rs[k],mid+1,r,mid+1,y,p);
    36     }
    37 }
    38 void query(int k,int l,int r,int x){
    39     if(!k)return;
    40     ans = ((ll)ans*lz[k] + (ll)(mod+1-ans)*(mod+1-lz[k]))%mod;
    41     if(l==r)return;
    42     else{
    43         int mid=(l+r)>>1;
    44         if(x<=mid)query(Ls[k],l,mid,x);
    45         else query(Rs[k],mid+1,r,x);
    46     }
    47 }
    48 void Update(int&k,int l,int r,int x1,int x2,int y1,int y2,int p){
    49     if(!k)k=++sz1;
    50     if(l==x1&&r==x2)update(rt[k],1,n,y1,y2,p);
    51     else{
    52         int mid=(l+r)>>1;
    53         if(x2<=mid)Update(ls[k],l,mid,x1,x2,y1,y2,p);
    54         else if(x1>mid)Update(rs[k],mid+1,r,x1,x2,y1,y2,p);
    55         else Update(ls[k],l,mid,x1,mid,y1,y2,p),Update(rs[k],mid+1,r,mid+1,x2,y1,y2,p);
    56     }
    57 }
    58 void Query(int k,int l,int r,int x,int y){
    59     if(!k)return;
    60     query(rt[k],1,n,y);
    61     if(l==r)return;
    62     else{
    63         int mid=(l+r)>>1;
    64         if(x<=mid)Query(ls[k],l,mid,x,y);
    65         else Query(rs[k],mid+1,r,x,y);
    66     }
    67 }
    68 int cnt,a[N];
    69 inline void print(int x){
    70     if(!x){putchar(48);putchar(10);return;}
    71     cnt=0;
    72     while(x)a[++cnt]=x%10,x/=10;
    73     for(rg int i=cnt;i;--i)putchar(a[i]+48);
    74     putchar(10);
    75 }
    76 int main(){
    77     #ifndef ONLINE_JUDGE
    78     freopen("T2.in","r",stdin);
    79     freopen("T2.out","w",stdout);
    80     #endif
    81     n=rd();m=rd();
    82     for(rg int i=1,l,r,op;i<=m;++i){
    83         op=rd();l=rd();r=rd();
    84         if(op==1){
    85             int p = inv(r-l+1);
    86             if(l>1)Update(Rt,1,n,1,l-1,l,r,p),update(rt[0],1,n,1,l-1,1);
    87             if(r<n)Update(Rt,1,n,l,r,r+1,n,p),update(rt[0],1,n,r+1,n,1);
    88             if(l<r)Update(Rt,1,n,l,r,l,r,p*2%mod),update(rt[0],1,n,l,r,(mod+1-p)%mod);
    89         }else{
    90             ans=1;
    91             if(l==1)query(rt[0],1,n,r);
    92             else Query(Rt,1,n,l-1,r);
    93             print(ans);
    94         //    printf("%d\n",ans);
    95         }
    96     }
    97 //    printf("%.2lf\n",1.0*clock()/CLOCKS_PER_SEC);
    98     return 0;
    99 }

    bzoj4785

原文地址:https://www.cnblogs.com/Paul-Guderian/p/10307005.html

时间: 2024-11-10 02:03:35

bzoj4785【Zjoi2017】树状数组的相关文章

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

4785: [Zjoi2017]树状数组 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 297  Solved: 195[Submit][Status][Discuss] Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道 基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1)

【BZOJ4785】[Zjoi2017]树状数组 树套树(二维线段树)

[BZOJ4785][Zjoi2017]树状数组 Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1) mod 2. 2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r 尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做.当时非常yo

【bzoj4785】[Zjoi2017]树状数组 线段树套线段树

题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的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

Zjoi2017树状数组

2.1 题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的 OI 比赛经历.那是一道基础的树状数组题. 给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: ? 1 x,表示将 Ax 变成 (Ax + 1) mod 2. ? 2 l r,表示询问 ( ∑r i=l Ai) mod 2. 尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做. 当时非常 young 的她写了如下的算法: 1: functio

ZJOI2017 树状数组

属于可怜出的小清新数据结构题呢 题目链接 解析 因为全部都在模\(2\)意义下,因此相当于单点异或,查询区间异或和. 如果你对树状数组足够熟悉,那么你会发现可怜写了一个单点加求后缀和的程序. 因此\([l,r]\)正确的概率就要使\(a_{l-1}\oplus a_l\oplus a_{l+1}\oplus ...\oplus a_n=a_r\oplus a_{r+1}\oplus...\oplus a_n\) 即\(a_{l-1}=a_r\) 于是我们用线段树维护这个问题好像就做完了 当然不对

「ZJOI2017」树状数组

「ZJOI2017」树状数组 以下均基于模2意义下,默认\(n,m\)同阶. 熟悉树状数组的应该可以发现,这题其实是求\(l-1\)和\(r\)位置值相同的概率. 显然\(l=1\)的情况需要特盘. 大暴力 对于\(l=1\)的情况,可以发现一个操作不会产生影响当且仅当增加\(r\)的值,而其他情况会改变\(l-1\)或\(r\). 对于\(l!=1\)的情况: ? 针对一次修改区间\([ql,qr]\). \([ql,qr]\)包含\(l-1,r\),那么有\(\displaystyle 2

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

(POJ 3067) Japan (慢慢熟悉的树状数组)

Japan Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 29295   Accepted: 7902 Description Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Japan is tall island with N cities on the East coas

【二维树状数组】See you~

https://www.bnuoj.com/v3/contest_show.php?cid=9148#problem/F [题意] 给定一个矩阵,每个格子的初始值为1.现在可以对矩阵有四种操作: A x y n1 :给格点(x,y)的值加n1 D x y n1: 给格点(x,y)的值减n1,如果现在格点的值不够n1,把格点置0 M x1 y1 x2 y2:(x1,y1)移动给(x2,y2)n1个 S x1 y1 x2 y2 查询子矩阵的和 [思路] 当然是二维树状数组 但是一定要注意:lowbi