1. 程式人生 > >【分塊,莫隊】【P4396】【AHOI2013】作業

【分塊,莫隊】【P4396】【AHOI2013】作業

傳送門

Description

此時己是凌晨兩點,剛剛做了Codeforces的小A掏出了英語試卷。英語作業其實不算多,一個小時剛好可以做完。然後是一個小時可以做完的數學作業,接下來是分別都是一個小時可以做完的化學,物理,語文......小A壓力巨大。

這是小A碰見了一道非常噁心的數學題,給定了一個長度為n的數列和若干個詢問,每個詢問是關於數列的區間表示數列的第\(l\)個數到第\(r\)個數),首先你要統計該區間內大於等於\(a\),小於等於\(b\)的數的個數,其次是所有大於等於\(a\),小於等於\(b\)的,且在該區間中出現過的數值的個數。

小A望著那數萬的資料規模幾乎絕望,只能向大神您求救,請您幫幫他吧。

Input

第一行是兩個正整數\(n,m\)

第二行\(n\)個數代表序列

接下來\(m\)行詢問,每行四個整數\(l,r,a,b\)

Output

對每個詢問輸出一行兩個用空格隔開的整數,分別代表答案

Hint

\(Forall:\)

\(1~\leq~n,m~\leq100000\)

Solution

多次查詢沒有修改,於是考慮莫隊

一個非常顯然的想法是對每個值的出現次數開桶,對第二問開權值桶,然後用樹狀陣列維護字首和,就可以做到單點修改區間查詢了。複雜度\(O(n~\sqrt{n}~logm+mlogm)\),其中\(O(n~\sqrt{n}~logm)\)是修改複雜度,\(O(mlogm)\)

是查詢複雜度。於是發現前面一項複雜度過高,而後面一項完全不需要做到這樣的複雜度。在一個操作複雜度過高,另一個操作複雜度壓縮過度時,可以考慮將其中一個的複雜度降低,提升另一個的複雜度。比如這裡考慮使用分塊維護桶,單次修改複雜度\(O(1)\),查詢複雜度\(O(\sqrt{n})\),總複雜度\(~O(~(n+m)~\sqrt{n}~)~\),可以通過本題

Code

#include<cmath>
#include<cstdio>
#include<algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a,b,c)
#endif
#define rg register
#define ci const int
#define cl const long long

typedef long long int ll;

template <typename T>
inline void qr(T &x) {
    rg char ch=getchar(),lst=' ';
    while((ch > '9') || (ch < '0')) lst=ch,ch=getchar();
    while((ch >= '0') && (ch <= '9')) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(lst == '-') x=-x;
}

namespace IO {
    char buf[120];
}

template <typename T>
inline void qw(T x,const char aft,const bool pt) {
    if(x < 0) {x=-x,putchar('-');}
    rg int top=0;
    do {IO::buf[++top]=x%10+'0';} while(x/=10);
    while(top) putchar(IO::buf[top--]);
    if(pt) putchar(aft);
}

const int maxn = 100010;

int n,m;
int MU[maxn],belong[maxn],sa[maxn],bsa[maxn],sb[maxn],bsb[maxn];

struct Ask {
    int l,r,a,b,num;
    ll ans1,ans2;
    inline bool operator<(const Ask &_others) const {
        if(belong[this->l] != belong[_others.l]) return this->l < _others.l;
        if(belong[this->l] & 1) return this->r < _others.r;
        return this->r > _others.r;
    }
};
Ask ask[maxn];

inline bool cmp(const Ask &_a,const Ask &_b) {
    return _a.num < _b.num;
}

int main() {
    qr(n);qr(m);
    for(rg int i=1;i<=n;++i) qr(MU[i]);
    for(rg int i=1;i<=m;++i) {
        qr(ask[i].l);qr(ask[i].r);qr(ask[i].a);qr(ask[i].b);ask[i].num=i;
    }
    for(rg int i=1,sn=sqrt(n);i<=n;++i) belong[i]=i/sn;
    std::sort(ask+1,ask+1+m);
    int prel=ask[1].l,prer=ask[1].l-1;
    for(rg int i=1;i<=m;++i) {
    
        int l=ask[i].l,r=ask[i].r;
        while(prel < l) {
            if(!(--sa[MU[prel]])) --sb[MU[prel]],--bsb[belong[MU[prel]]];
            --bsa[belong[MU[prel]]];
            ++prel;
        }
        while(prel > l) {
            --prel;
            if(!sa[MU[prel]]) ++sb[MU[prel]],++bsb[belong[MU[prel]]];
            ++bsa[belong[MU[prel]]];++sa[MU[prel]];
        }
        while(prer < r) {
            ++prer;
            if(!sa[MU[prer]]) ++sb[MU[prer]],++bsb[belong[MU[prer]]];
            ++bsa[belong[MU[prer]]];++sa[MU[prer]];
        }
        while(prer > r) {
            if(!(--sa[MU[prer]])) --sb[MU[prer]],--bsb[belong[MU[prer]]];
            --bsa[belong[MU[prer]]];
            --prer;
        }
        ll &ans1=ask[i].ans1,&ans2=ask[i].ans2;
        int &a=ask[i].a,&b=ask[i].b;
        int bl=belong[ask[i].a],br=belong[ask[i].b];
        if(bl == br) {
            for(rg int i=a;i<=b;++i) ans1+=sa[i],ans2+=sb[i];
            continue;
        }
        for(rg int i=bl+1;i<br;++i) ans1+=bsa[i],ans2+=bsb[i];
        for(rg int i=a;belong[i] == bl;++i) ans1+=sa[i],ans2+=sb[i];
        for(rg int i=b;belong[i] == br;--i) ans1+=sa[i],ans2+=sb[i];
    }
    std::sort(ask+1,ask+1+m,cmp);
    for(rg int i=1;i<=m;++i) {
        qw(ask[i].ans1,' ',true);
        qw(ask[i].ans2,'\n',true);
    }
    return 0;
}

Summary

修改複雜度過高,查詢複雜度壓縮過度時,考慮使用分塊平衡複雜度。