洛谷 P2801 教主的魔法【分塊】

題目描述

教主最近學會了一種神奇的魔法,能夠使人長高。於是他準備演示給XMYZ信息組每個英雄看。於是N個英雄們又一次聚集在了一起,這次他們排成了一列,被編號爲1、2、……、N。

每個人的身高一開始都是不超過1000的正整數。教主的魔法每次可以把閉區間[L, R](1≤L≤R≤N)內的英雄的身高全部加上一個整數W。(雖然L=R時並不符合區間的書寫規範,但我們可以認爲是單獨增加第L(R)個英雄的身高)

CYZ、光哥和ZJQ等人不信教主的邪,於是他們有時候會問WD閉區間 [L, R] 內有多少英雄身高大於等於C,以驗證教主的魔法是否真的有效。

WD巨懶,於是他把這個回答的任務交給了你。

輸入格式

第1行爲兩個整數N、Q。Q爲問題數與教主的施法數總和。

第2行有N個正整數,第i個數代表第i個英雄的身高。

第3到第Q+2行每行有一個操作:

(1) 若第一個字母爲“M”,則緊接着有三個數字L、R、W。表示對閉區間 [L, R] 內所有英雄的身高加上W。

(2) 若第一個字母爲“A”,則緊接着有三個數字L、R、C。詢問閉區間 [L, R] 內有多少英雄的身高大於等於C。

輸出格式

對每個“A”詢問輸出一行,僅含一個整數,表示閉區間 [L, R] 內身高大於等於C的英雄數。

思路:我們用分塊來寫這道題,分好塊後將塊內的元素排序,然後記錄一下位置,對於修改操作來說,我們可以用直接修改,對於查詢操作的話端點的塊暴力找,中間的由於元素有序,我們可以二分來找,然後計算出該塊的數量,就可以得到答案了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
struct node
{
    ll val;
    int id;
    bool operator < (const node &r) const
    {
        return val < r.val;
    }
}a[maxn];
ll ad[maxn], ID[maxn]; //sum[i]第i塊的和,ad[i]第i塊的增量
ll L[maxn], R[maxn], pos[maxn]; //每段左右端點和每個位置屬於哪一段
int n, m, k; //k個塊

int read()
{
    int x = 0, w = 1;
    char ch = 0;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
            w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * w;
}
ll slove(int l, int r, ll d, ll x) //二分查找
{
    int p = r + 1, pp = r;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(a[mid].val + x >= d) r = mid - 1;
        else l = mid + 1;
    }
    return pp - r;
}
void add(int l, int r, ll d) //區間[l, r]加上d
{
    int p = pos[l], q = pos[r];
    if(p == q) //同塊
    {
        for(int i = l; i <= r; ++i) a[ID[i]].val += d;
        sort(a + L[p], a + R[p] + 1);
        for(int i = L[p]; i <= R[p]; ++i)
            ID[a[i].id] = i;
    }
    else
    {
        for(int i = p + 1; i <= q - 1; ++i) ad[i] += d;
        for(int i = l; i <= R[p]; ++i) a[ID[i]].val += d;
        for(int i = L[q]; i <= r; ++i) a[ID[i]].val += d;
        sort(a + l, a + L[p] + 1);
        sort(a + R[q], a + r + 1);
        for(int i = L[p]; i <= R[p]; ++i) ID[a[i].id] = i;
        for(int i = L[q]; i <= R[q]; ++i) ID[a[i].id] = i;
    }
}
ll ask(int l, int r, ll d)
{
    int p = pos[l], q = pos[r];
    ll ret = 0;
    if(p == q)
    {
        for(int i = l; i <= r; ++i)
            if(a[ID[i]].val + ad[p] >= d)
                ++ret;
    }
    else
    {
        for(int i = l; i <= R[p]; ++i) if(a[ID[i]].val + ad[p] >= d) ++ret;
        for(int i = L[q]; i <= r; ++i) if(a[ID[i]].val + ad[q] >= d) ++ret;
        for(int i = p + 1; i <= q - 1; ++i)
            ret += slove(L[i], R[i], d, ad[i]);
    }
    return ret;
}

int main()
{
    n = read(), m = read();
    for(int i = 1; i <= n; ++i) a[i].val = read(), a[i].id = i;
    k = sqrt(n); //分成k塊
    for(int i = 1; i <= k; ++i)
    {
        L[i] = (i - 1) * k + 1;
        R[i] = i * k;
    }
    if(R[k] < n) //最後有一小塊
    {
        ++k;
        L[k] = R[k - 1] + 1;
        R[k] = n;
    }
    for(int i = 1; i <= k; ++i)
    {
        sort(a + L[i], a + R[i] + 1); //對每一段排序
        for(int j = L[i]; j <= R[i]; ++j)
        {
            pos[j] = i;
            ID[a[j].id] = j; //記錄位置
        }
    }
    while(m--)
    {
        char op[3];
        int l, r;
        ll d;
        scanf("%s", op);
        l = read(), r = read(), d = read();
        if(op[0] == 'M')
        {
            add(l, r, d);
        }
        else if(op[0] == 'A')
            printf("%lld\n", ask(l, r, d));
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章