【分塊,莫隊】【P4396】【AHOI2013】作業
阿新 • • 發佈:2018-11-23
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)\)
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
修改複雜度過高,查詢複雜度壓縮過度時,考慮使用分塊平衡複雜度。