【線段樹】【樹】[BZOJ4293][HYSBZ4293][PA2015]Siano

題目描述

注:此題爲BZOJ的權限題目

Description

農夫Byteasar買了一片n畝的土地,他要在這上面種草。
他在每一畝土地上都種植了一種獨一無二的草,其中,第i畝土地的草每天會長高a[i]釐米。
Byteasar一共會進行m次收割,其中第i次收割在第d[i]天,並把所有高度大於等於b[i]的部分全部割去。Byteasar想知道,每次收割得到的草的高度總和是多少,你能幫幫他嗎?

Input

第一行包含兩個正整數n,m(1<=n,m<=500000),分別表示畝數和收割次數。
第二行包含n個正整數,其中第i個數爲ai,依次表示每畝種植的草的生長能力。
接下來m行,每行包含兩個正整數d[i],bi,依次描述每次收割。
數據保證d[1]

Output

輸出m行,每行一個整數,依次回答每次收割能得到的草的高度總和。

樣例輸入

4 4
1 2 4 3
1 1
2 2
3 0
4 4

樣例輸出

6
6
18
0

題目分析

就這麼說吧,我覺得這題的代碼難度比這題的思維難度要難。我們可以很容易的發現,如果我們首先將所有的土地按照雜草的生長速度排序之後進行求解得到的答案其實是不變的,那麼我們第一步先排序,然後我們發現無論生長到第幾天,雜草的高度序列總是不下降的,同理有割掉草之後,繼續生長雜草的序列仍然是不下降的,那麼我們可以很容易的得到算法:

  1. 首先生長後,在線段樹上二分找到我們另當前雜草大於詢問的位置
  2. 輸出=(所有雜草高度之和-(大於的土地數量*詢問的高度))
  3. 將得到的位置後面的雜草(因爲全部高於當前)賦值爲當前詢問的高度

這裏都看上去很正常,複雜度似乎也可以接受O(nlogn) ,那麼我們開始寫。。。寫着寫着我們發現如果要統計區間每一天增加多少怎麼辦。。。可以統計當前給定Ai 的前綴和di 對於一個區間[l,r] 我們有每天生長(drdl1) 那麼就可以得到c 天后生長多少了,但是我們同樣可以發現,我們還需要維護一個b 表示當前的樹我們需要賦值成多少。。。可以寫了?不能
我們可以發現,當子樹中存在一個b 同時當前也有一個c 要push_down的時候,我們兩邊都不能拋棄那麼我們令a 表示是否保留當前值那麼我們對於每一段就有

tree[u].sum=tree[u].sum×tree[u].a+(tree[u].rtree[u].l+1)×tree[u].b+tree[u].c×(dtree[u].rdtree[u].l1)
同理最大值一定位於區間右邊那麼最大值爲
tree[u].Max=tree[u].Max×tree[u].a+tree[u].b+tree[u].c×Atree[u].r
但是我們對於處理下方就要分類討論了,發現如果當前爲u ,下一層爲uson 那麼我們可以發現當tree[u].a==0 的時候我們有完全覆蓋,那麼可以直接
tree[uson].a=0tree[uson].b=tree[u].btree[uson].c=tree[u].c
然後根據上面的公式計算出sumMax

然後我們考慮當tree[u].a==1 的情況,我們可以發現1 代表保留原來的內容,那麼我們保持原來的a 不變,bc 累加(因爲滿足分配律)所以我們可以得到

tree[uson].b=tree[uson].b+tree[u].btree[uson].c=tree[uson].c+tree[u].c
那麼我們可以發現這個時候增加的b 其實只有tree[u].b 也可以發現增加的c 也只有tree[u].c 的部分,那麼
tree[uson].Max=tree[uson].Max+tree[u].b+tree[u].c×Atree[uson].rtree[uson].sum=tree[uson].sum+tree[u].b×(tree[uson].rtree[uson].l+1)+tree[u].c×(dtree[uson].rdtree[uson].l1)

當然對於左右區間搞完之後就可以將當前的設置爲

tree[u].a=1tree[u].b=0tree[u].c=0

最艱難的部分已經過去push_up就和平時的push_up沒有什麼不同。可以開始寫了。

代碼

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 500000;
long long a[MAXN+10], up[MAXN+10];
int aa, cc;
long long bb;
struct Tree{
    int l, r;
    long long Max, sum, b, c;
    bool aa;
    void operator+=(const Tree& an){
        if(an.aa){
            b += an.b; c += an.c;
            Max += an.b + an.c*a[r];
            sum += an.b*(r-l+1)+an.c*(up[r]-up[l-1]);
        }else{
            aa = 0; b = an.b; c = an.c;
            Max = b + c * a[r];
            sum = b * (r - l + 1) + (up[r] - up[l-1]) * c;
        }
    }
}tree[MAXN*4+10];
void recalc(int u, int l, int r){
    tree[u].sum = tree[u].sum * tree[u].aa + tree[u].b * (r - l + 1) + tree[u].c * (up[r] - up[l-1]);
    tree[u].Max = tree[u].Max * tree[u].aa + tree[u].b + tree[u].c * up[r];
}
void push_down(int u){
    tree[u*2+1] += tree[u];
    tree[u*2] += tree[u];
    tree[u].aa = 1, tree[u].b = 0, tree[u].c = 0;
}
void push_up(int u){
    tree[u].Max = tree[u*2+1].Max;
    tree[u].sum = tree[u*2].sum + tree[u*2+1].sum;
    tree[u].aa = 1;
    tree[u].b = tree[u].c = 0;
}
int Findpos(int u, long long v){
    if(tree[u].l == tree[u].r) return tree[u].l;
    push_down(u);
    if(tree[u*2].Max >= v) return Findpos(u*2, v);
    return Findpos(u*2+1, v);
}
long long query(int u, int pos){
    if(pos <= tree[u].l) return tree[u].sum;
    int mid=(tree[u].l+tree[u].r)>>1;
    push_down(u);
    long long ret = 0;
    if(pos <= mid) ret += query(u*2, pos);
    ret += query(u*2+1, pos);
    return ret;
}
void reset(int u, int pos, long long v){
    if(pos <= tree[u].l){
        tree[u].aa = tree[u].c = 0; tree[u].b = v;
        tree[u].Max = v;
        tree[u].sum = (tree[u].r - tree[u].l + 1) * v;
        return ;
    }
    int mid = (tree[u].l + tree[u].r) >> 1;
    push_down(u);
    reset(u*2+1, pos, v);
    if(pos <= mid) reset(u*2, pos, v);
    push_up(u);
}
void build(int u, int l, int r){
    tree[u].l = l, tree[u].r = r;
    tree[u].aa = 1;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(u*2, l, mid);
    build(u*2+1, mid+1, r);
}
int main(){
    int n, m;
    long long d, b, ud=0;
    scanf("%d%d", &n, &m);
    build(1, 1, n);
    for(int i=1;i<=n;i++) scanf("%I64d", &a[i]);
    sort(a+1, a+1+n);
    for(int i=1;i<=n;i++) up[i] = up[i-1] + a[i];
    for(int i=1;i<=m;i++){
        scanf("%I64d%I64d", &d, &b);
        long long day = d - ud;
        tree[1].c += day;
        tree[1].Max += day * a[n];
        tree[1].sum += day * up[n];
        if(tree[1].Max <= b){
            ud = d;
            printf("0\n");
            continue;
        }
        long long pos = Findpos(1, b);
        printf("%I64d\n", query(1, pos) - 1LL * b * (n-pos+1));
        reset(1, pos, b);
        ud = d;
    }

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