1. 程式人生 > 其它 >C. Optimal Insertion 題解(莫隊 區間眾數問題)

C. Optimal Insertion 題解(莫隊 區間眾數問題)

分塊

分塊

先看我們線上段樹, 樹狀陣列中說爛了的模板題


Description

給定一個長度為 \(N\) 的數列 \(A\) ,以及 \(M\) 條指令,每條指令可能是以下兩種之一:

C l r d,表示把 \(A[l],A[l+1],…,A[r]\) 都加上 \(d\)
Q l r,表示詢問數列中第 \(l∼r\) 個數的和。
對於每個詢問,輸出一個整數表示答案。


顧名思義, 分塊非常形象的理解就是分成一塊一塊。

對於零碎的塊, 我們暴力解決。
對於完整的塊, 我們直接加就好了。


分塊

int get(int x) 
{ 
    return x/ len; 
}
len= sqrt(n);       //len是塊的長度
for(int i= 1; i<= n; i++ ) scanf("%d", &a[i]); 
for(int i= 1; i<= n; i++ ) s[get(i)]+= a[i], siz[get(i)]++ ;    //s表示和, 也就是我們記錄的資訊

修改

void change(int l, int r, int d)
{
    if(get(l)== get(r))
    {
        for(int i= l; i<= r; i++ ) a[i]+= d, s[get(i)]+= d;
        return ;
    }
    int i= l, j= r;
    while(get(i)== get(l)) a[i]+= d, s[get(i)]+= d, i++ ;     //找到整的塊
    while(get(j)== get(r)) a[j]+= d, s[get(j)]+= d, j-- ;
    for(int k= get(i); k<= get(j); k++ ) s[k]+= (ll)siz[k]* d, add[k]+= d;     //add是類似懶標記的東西, 有些時候不用一個個加
    return ;
}

查詢

//可以發現是上面的改改。

ll query(int l, int r)
{
    if(get(l)== get(r))
    {
        ll ans= 0;
        for(int i= l; i<= r; i++ ) ans+= (ll)a[i]+ add[get(i)]; 
        return ans;
    }
    ll ans= 0;
    int i= l, j= r;
    while(get(i)== get(l)) ans+= (ll)a[i]+ add[get(i)], i++ ;
    while(get(j)== get(r)) ans+= (ll)a[j]+ add[get(j)], j-- ;
    for(int k= get(i); k<= get(j); k++ ) ans+= s[k];
    return ans;
}
未來可期。