1. 程式人生 > >洛谷 P3924 康娜的線段樹 解題報告

洛谷 P3924 康娜的線段樹 解題報告

葉子 操作 shu scan 分享圖片 解題報告 累加 pan 之前

P3924 康娜的線段樹

題目描述

小林是個程序媛,不可避免地康娜對這種人類的“魔法”產生了濃厚的興趣,於是小林開始教她\(OI\)

技術分享圖片

今天康娜學習了一種叫做線段樹的神奇魔法,這種魔法可以維護一段區間的信息,是非常厲害的東西。康娜試著寫了一棵維護區間和的線段樹。由於她不會打標記,因此所有的區間加操作她都是暴力修改的。具體的代碼如下:

struct Segment_Tree{
#define lson (o<<1)
#define rson (o<<1|1)
    int sumv[N<<2],minv[N<<2];
    inline void pushup(int o){sumv[o]=sumv[lson]+sumv[rson];}
    inline void build(int o,int l,int r){
        if(l==r){sumv[o]=a[l];return;}
        int mid=(l+r)>>1;
        build(lson,l,mid);build(rson,mid+1,r);
        pushup(o);
    }
    inline void change(int o,int l,int r,int q,int v){
        if(l==r){sumv[o]+=v;return;}
        int mid=(l+r)>>1;
        if(q<=mid)change(lson,l,mid,q,v);
        else change(rson,mid+1,r,q,v);
        pushup(o);
    }
}T; 

在修改時,她會這麽寫:

for(int i=l;i<=r;i++)T.change(1,1,n,i,addv);

顯然,這棵線段樹每個節點有一個值,為該節點管轄區間的區間和。

康娜是個愛思考的孩子,於是她突然想到了一個問題:

如果每次在線段樹區間加操作做完後,從根節點開始等概率的選擇一個子節點進入,直到進入葉子結點為止,將一路經過的節點權值累加,最後能得到的期望值是多少?

康娜每次會給你一個值\(qwq\),保證你求出的概率乘上\(qwq\)是一個整數。

這個問題太簡單了,以至於聰明的康娜一下子就秒了。

現在她想問問你,您會不會做這個題呢?

輸入輸出格式

輸入格式:

第一行整數\(n,m,qwq\)

表示線段樹維護的原序列的長度,詢問次數,分母。

第二行\(n\)個數,表示原序列。

接下來\(m\)行,每行三個數\(l,r,x\)表示對區間\([l,r]\)加上\(x\)

輸出格式:

\(m\)行,表示期望的權值和乘上\(qwq\)結果。

說明

對於30%的數據,保證 \(1 \leq n,m \leq 100\)

對於70%的數據,保證 \(1 \leq n,m, \leq 10^{5}\)

對於100%的數據,保證 \(1 \leq n,m \leq 10^6\)

\(-1000 \leq a_i,x \leq 1000\)


其實題目不難,然而我概率期望學的差,還是不怎麽會做。

我們發現,其實每個葉子節點的貢獻的不會變的,則第\(i\)

個葉子節點貢獻的次數是它之前的所有包含它的區間的貢獻次數之和。

根據條件概率,每一個大區間出現的概率都是它的子區間的兩倍,所以我們以最小的區間算做1,統計每個葉子節點的貢獻次數,最後再除以\(\lceil logn \rceil\)即可。

具體實現可以直接模擬建樹統計。

然後我們發現操作只有區間加和全局詢問。

區間加我們可以通過葉子節點貢獻次數前綴和維護全局偏移量。

復雜度:\(O(nlogn+m)\)


Code:

#include <cstdio>
#define ll long long
ll max(ll x,ll y){return x>y?x:y;}
const ll N=1000010;
ll dat[N],cnt[N],f[N],ans,QAQ,n,m,d,dep[N];
void build(ll l,ll r,ll Dep)
{
    if(l==r)
    {
        dep[l]=Dep;
        d=max(d,Dep);
        return;
    }
    ll mid=l+r>>1;
    build(l,mid,Dep+1);
    build(mid+1,r,Dep+1);
}
void init()
{
    scanf("%lld%lld%lld",&n,&m,&QAQ);
    build(1,n,1);
    for(ll i=1;i<=n;i++)
    {
        if(dep[i]==d)
            cnt[i]=(1<<d)-1;
        else
            cnt[i]=(1<<d)-2;
        scanf("%lld",dat+i);
        f[i]=f[i-1]+cnt[i];
        ans+=cnt[i]*dat[i];
    }
}
void work()
{
    ll l,r,x;
    d=1<<d-1;
    for(ll i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld",&l,&r,&x);
        ans+=(f[r]-f[l-1])*x;
        printf("%lld\n",(QAQ/d*ans));
    }
}
int main()
{
    init();
    work();
    return 0;
}

2018.7.21

洛谷 P3924 康娜的線段樹 解題報告