1. 程式人生 > >【BZOJ4785】樹狀陣列(ZJOI2017)-概率+二維線段樹+動態開點

【BZOJ4785】樹狀陣列(ZJOI2017)-概率+二維線段樹+動態開點

測試地址:樹狀陣列
做法:本題需要用到概率+二維線段樹+動態開點。
首先分析題目,對樹狀陣列結構熟悉的同學(不熟悉的話…畫一畫或者打個表也行)就能看出,題目中的資料結構求的是字尾和。那麼當我們詢問[l,r]時,我們原來是算[1,l1]xor[1,r]=[l,r],現在變成算[l1,n]xor[r,n]=[l1,r1],那麼這兩個區間的唯一區別就是l1r這兩個點,那麼我們算出的區間值相同的概率,就等於這兩個點的值相同的概率,於是我們只要算出這個概率即可。
一個直觀的思路是維護每個點為

1的概率,但我們發現在每次修改時,只能在區間中選擇一個點修改,這就意味著如果一個點被修改,另一個就不能被修改,也就是說它們之間的概率不是獨立的,因此不能計算。
因此我們考慮直接維護一個二維矩陣,每個點(x,y)上的元素表示點x和點y值相同的概率,一開始均為1。那麼對於每次修改,有一個點在修改區間中的點對有1rl+1的概率相同性發生變化,有兩個點在修改區間中的點對有2rl+1的概率相同性發生變化(因為一次只能修改一個點)。接下來考慮維護這樣的修改,對於一個點對,令該點對原先相同概率為p,修改後相同性不變的概率為q,那麼修改後的p=pq+(1p)(1q)。可以證明當兩個修改作用在同一個點上時,這兩個修改的順序不影響結果(這個把式子展開就可以看得出來了),並且兩次修改後不變的概率可以合併成q=q1q2+(1q1)(1q2)。這就顯然可以用二維線段樹來維護修改了。
接下來,觀察到題目的性質是子矩陣修改,單點詢問,於是利用標記永久化的思想,每次直接把修改標記記錄在對應的節點上,那麼詢問一個點時,就直接把包含這個點的所有線段樹節點上的標記合併起來即可,時間複雜度為O(nlog2n),空間複雜度用動態開點可以做到O(nlog2n
)

如果你只考慮到了上面這一些,你還是會面臨爆0的結局,因為有一種特殊的詢問情況:l=1。注意到在呼叫樹狀陣列中的函式時,如果l1=0就會立刻返回0,而不是返回一個區間的值,那麼實際上變成了求區間[1,r][r,n]奇偶性相同的概率。於是在修改區間[l,r]時,x在區間[1,l1][r+1,n]中時,[1,x][x,n]的相同性一定變化,否則如果x在區間[l,r]內,[1,x][x,n]的相同性就有1rl+1的概率不變,另開一個線段樹用和上面類似的方法維護即可。
以下是本人程式碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=100010;
int n,m,tot=0,rt[4*N]={0},ch[300*N][2]={0};
ll val[300*N],x;

ll inv(ll x)
{
    ll s=1,ss=x,b=mod-2;
    while(b)
    {
        if (b&1) s=s*ss%mod;
        ss=ss*ss%mod;b>>=1;
    }
    return s;
}

void modify(int &no,int l,int r,int s,int t,ll p)
{
    if (s>t) return;
    if (!no)
    {
        no=++tot;
        val[no]=1;
    }
    if (l>=s&&r<=t)
    {
        val[no]=((val[no]*p+(1-val[no])*(1-p))%mod+mod)%mod;
        return;
    }
    int mid=(l+r)>>1;
    if (s<=mid) modify(ch[no][0],l,mid,s,t,p);
    if (t>mid) modify(ch[no][1],mid+1,r,s,t,p);
}

void query(int no,int l,int r,int pos)
{
    if (!no) return;
    x=((x*((val[no]<<1)-1)+(1-val[no]))%mod+mod)%mod;
    if (l==r) return;
    int mid=(l+r)>>1;
    if (pos<=mid) query(ch[no][0],l,mid,pos);
    else query(ch[no][1],mid+1,r,pos);
}

void Modify(int no,int l,int r,int s1,int t1,int s2,int t2,ll p)
{
    if (s1>t1||s2>t2) return;
    if (l>=s1&&r<=t1)
    {
        modify(rt[no],1,n,s2,t2,p);
        return;
    }
    int mid=(l+r)>>1;
    if (s1<=mid) Modify(no<<1,l,mid,s1,t1,s2,t2,p);
    if (t1>mid) Modify(no<<1|1,mid+1,r,s1,t1,s2,t2,p);
}

void Query(int no,int l,int r,int pos1,int pos2)
{
    query(rt[no],1,n,pos2);
    if (l==r) return;
    int mid=(l+r)>>1;
    if (pos1<=mid) Query(no<<1,l,mid,pos1,pos2);
    else Query(no<<1|1,mid+1,r,pos1,pos2);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int op,l,r;
        scanf("%d%d%d",&op,&l,&r);
        if (op==1)
        {
            ll p=inv(r-l+1);
            Modify(1,1,n,1,l-1,l,r,(1-p+mod)%mod);
            Modify(1,1,n,l,r,r+1,n,(1-p+mod)%mod);
            Modify(1,1,n,l,r,l,r,(1-(p<<1)+(mod<<1))%mod);
            modify(rt[0],
            
           

相關推薦

BZOJ4785陣列ZJOI2017-概率+線段+動態

測試地址:樹狀陣列 做法:本題需要用到概率+二維線段樹+動態開點。 首先分析題目,對樹狀陣列結構熟悉的同學(不熟悉的話…畫一畫或者打個表也行)就能看出,題目中的資料結構求的是字尾和。那麼當我們詢問[l,r][l,r]時,我們原來是算[1,l−1]xor[1,

BZOJ4785 ZJOI2017數組概率+線段

cal cor += clas str col else 個數 clu   可以發現這個寫掛的樹狀數組求的是後綴和。find(r)-find(l-1)在模2意義下實際上查詢的是l-1~r-1的和,而本來要查詢的是l~r的和。也就是說,若結果正確,則a[l-1]=a[r](m

資料結構-陣列

學習筆記-樹狀陣列(三) 樹狀陣列(一) 樹狀陣列(二) 通過樹狀陣列的基本操作,我們可以實現區間查詢和單點修改。結合差分,又可以實現單點查詢和區間修改。那麼,怎麼才能像線段樹一樣,快速實現區間查詢,區間修改呢? 由差分到字首和 既然要區間修改,那麼一定要使用差分陣列而不是原始陣列 由上一篇可見,

陣列 uva1428

N <tex2html_verbatim_mark>(3N20000) <tex2html_verbatim_mark>ping pong players live along a west-east street(consider the stree

陣列 模板

struct szsz { int c[maxn]; int lowbit(int x) { return x&(-x); } int getadd(int x) { int ans=0; while(x>0) { ans+

陣列

樹狀陣列定義 樹狀陣列陣列顧名思義它是樹狀的陣列 百度:樹狀陣列(Binary Indexed Tree(B.I.T), Fenwick Tree)是一個查詢和修改複雜度都為log(n)的資料結構。 樹狀陣列常用操作 一、神奇的lowbit 假設陣列a[1…n],那

陣列模板

題目分析 將一組陣列a[N] 輸入Query a b,輸出SUM(ai + …… + aj) 輸入Add i j,s[i] = s[i] + j 輸入Sub  i j,s[j] = s[i] -  j 陣列動態求和,明顯的樹狀陣列,呼叫樹狀陣列模版:樹狀

陣列BIT—— 一篇就夠了

# 樹狀陣列(BIT)—— 一篇就夠了 ## 前言、內容梗概 本文旨在講解: - [樹狀陣列的原理](#jump1)(起源,原理,模板程式碼與需要注意的一些知識點) - [樹狀陣列的優勢,缺點,與比較(eg:線段樹)](#jump2) - [樹狀陣列的經典例題及其技巧(普通離散化,二分查詢離散化)](#j

BZOJ4785[Zjoi2017]數組 線段

這也 現在 ont 平面 nbsp -s mil 比賽 turn 【BZOJ4785】[Zjoi2017]樹狀數組 Description 漆黑的晚上,九條可憐躺在床上輾轉反側。難以入眠的她想起了若幹年前她的一次悲慘的OI 比賽經歷。那是一道基礎的樹狀數組題。給出

BZOJ4785 [Zjoi2017]數組 線段 + 標記永久化

結合 題解 php 數組 air line pri www ostream 題目鏈接 BZOJ4785 題解 肝了一個下午QAQ沒寫過二維線段樹還是很難受 首先題目中的樹狀數組實際維護的是後綴和,這一點憑分析或經驗或手模觀察可以得出 在\(\mod 2\)意義下,我們實際求

模板陣列差分

題目描述 如題,已知一個數列,你需要進行下面兩種操作: 1.將某區間每一個數數加上x 2.求出某一個數的和 輸入輸出格式 輸入格式: 第一行包含兩個整數N、M,分別表示該數列數字的個數和操作的總個數。 第二行包含N個用空格分隔的整數,其中第i個數字表示數列第i項的初始值

BZOJ1818[CQOI2010]內部白點陣列,掃描線

【BZOJ1818】[CQOI2010]內部白點(樹狀陣列,掃描線) 題面 BZOJ 題解 不難發現\(-1\)就是在搞笑的。 那麼對於每一行,我們顯然可以處理出來最左和最右的點,那麼等價於我們在橫著的方向上得到了若干條線段,同理,在豎直方向上也得到了若干條線段,那麼最終的答案就是這些線段的交點個數加

專題陣列完整版

傳統陣列(共n個元素)的元素修改和連續元素求和的複雜度分別為O(1)和O(n)。樹狀陣列通過將線性結構轉換成偽樹狀結構(線性結構只能逐個掃描元素,而樹狀結構可以實現跳躍式掃描),使得修改和求和複雜度均為O(lgn),大大提高了整體效率。 給定序列(數列)A,我們設一個數組C滿足 C[i] = A[i–

洛谷 P3919 模板可持久化陣列可持久化線段/平衡-可持久化線段(單更新,單查詢)

P3919 【模板】可持久化陣列(可持久化線段樹/平衡樹) 題目背景 UPDATE : 最後一個點時間空間已經放大 標題即題意 有了可持久化陣列,便可以實現很多衍生的可持久化功能(例如:可持久化並查集) 題目描述 如題,你需要維護這樣的一個長度為 NN 的陣列,支援如下幾種操作

洛谷P3919 模板可持久化陣列可持久化線段/平衡

主席樹太費空間了,而本題也無需維護任何區間資訊,只是查詢歷史版本點值,沒必要構造可持久化線段樹,我們可以構建結構類似於 BST 的可持久化資料結構,這樣每個非葉節點也會帶有權值,不會被浪費掉 Code; #include<cstdio> #include<algorit

CF E. Vasya and a Tree dfs+樹狀陣列(給你一棵n個節點的,每個有一個權值,初始全為0,m次操作,每次三個數(v, d, x)表示只考慮以v為根的子,將所有與v距離小於等於d的權值全部加上x,求所有操作完畢後,所有節點的值

題意: 給你一棵n個節點的樹,每個點有一個權值,初始全為0,m次操作,每次三個數(v, d, x)表示只考慮以v為根的子樹,將所有與v點距離小於等於d的點權值全部加上x,求所有操作完畢後,所有節點的值   首先要明確兩件事情性質1.每個人的操作只會影響到他的子孫(包括自己) 性質1.每個人的操

Luogu P3919 模板可持久化陣列可持久化線段/平衡

題面跳轉站 題目背景 UPDATE : 最後一個點時間空間已經放大 標題即題意 有了可持久化陣列,便可以實現很多衍生的可持久化功能(例如:可持久化並查集) 題目描述 如題,你需要維護這樣的一個長度為 NN 的陣列,支援如下幾種操作 在某個歷史版

P3919 模板可持久化陣列可持久化線段/平衡

主席樹模板題,學了下主席樹的模板 主席樹的核心思想是每次儲存下來每次單點修改之後的線段樹的資料,但這樣肯定會mle,所以有一個優化的方法:因為每次只更新一個點,那麼在這個線段樹裡只有這個點所在的那一條鏈可能會修改,也就是說我們只要記錄這一條鏈就足夠了,其他的資料我們只要

洛谷P3379 模板最近公共祖先LCA鏈剖分

樹鏈剖分 turn 規模 一次 .org pen 整數 src namespace 題目描述 如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。 輸入輸出格式 輸入格式: 第一行包含三個正整數N、M、S,分別表示樹的結點個數、詢問的個數和樹根結點的

線段+掃描線p1884[Usaco12FEB]過度種植Overplanting …

Description 在一個笛卡爾平面座標系裡(則X軸向右是正方向,Y軸向上是正方向),有\(N(1<=N<=1000)\)個矩形,第i個矩形的左上角座標是\((x1, y1)\),右下角座標是\((x2,y2)\)。問這\(N\)個矩形所覆蓋的面積是多少?注意:被重複覆蓋的區域的面積