1. 程式人生 > >【分塊】LGP5113 Sabbat of the witch

【分塊】LGP5113 Sabbat of the witch

【題目】
原題地址
給定一個長度為 n n 的數列 a a ,要求支援三種操作:

  • 區間賦值為 x
    x
  • 區間求和
  • 撤銷某次賦值操作,即去掉那一次操作
    n , m 1 0
    5 n,m\leq 10^5
    ,操作 1 1 次數不超過 65000
    65000
    次,所有輸入不超過 1 0 9 10^9 ,強制線上。

【解題思路】
一道十分暴力的分塊題啊。
首先我們考慮一個比較 n a i v e naive O ( n n log n ) O(n\sqrt n \log n) 做法:首先對序列進行分塊。序列的每個位置維護一個 set \text{set} 記錄覆蓋了這個位置的所有修改,每個塊再維護一個 set \text{set} 記錄覆蓋了整個塊的所有修改。那麼接下來詢問操作我們也可以很方便地計算了。

具體來說:我們將塊內中的每個點按照最後有效操作時間排序,那麼一個塊的和可以這樣求:二分出來那些點的生效時間在塊的生效時間之後,假設這些點的和為 a a ,然後還可以得到有幾個點顏色被塊上的修改所覆蓋了,設這些點的個數為 b b ,當前塊上數字為 k k ,那麼塊中元素的和就是 k b + a kb+a

由於每次修改操作我們只需要對兩邊散點的塊重新排序,所以三種操作的複雜度都是 O ( n log n ) O(\sqrt n \log n) 的。

現在我們要將這個 log \log 去掉,考慮分塊的暴力性。
我們每個位置和塊上維護的 set \text{set} 實際上是沒有用的,因為插入的數字是單調的,所以我們只需要用一個棧來維護可以達到相同的效果。
排序方面,由於操作 1 1 的次數不超過 m = 65000 m=65000 次,我們可以考慮使用基數排序,在 m \lceil \sqrt m \rceil 進位制下,我們可以用 m \sqrt m 的時間進行排序。實際上我們取 m = 65536 m=65536 的話可以用 256 256 進位制簡單處理。
最後二分,我們發現如果塊的棧頂元素是在重構之後丟進去的話整個塊將會是一個值,否則棧頂就是重構之前的最大值,而顯然重構之前的最大值是單調不增的,維護一個指標每次暴力向左爬即可。

最後複雜度就是 O ( n n ) O(n\sqrt n) 的了,由於 n n 太大,甚至不能使用 STL \text{STL}

【參考程式碼】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e5+10,S=250,M=255;
int n,m,cnt;
ll ans;

namespace IO
{
    int read()
    {
        int ret=0;char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
        return ret;
    }
    void write(ll x)
    {
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    void writeln(ll x){write(x);putchar('\n');}
}
using namespace IO;

namespace Stack
{
    bool mark[N];
    int sz,head[N],nex[N*S],num[N*S];
    int top(int x){return num[head[x]];}
    void push(int x,int v){++sz;num[sz]=v;nex[sz]=head[x];head[x]=sz;}
    void pop(int x){while(mark[num[head[x]]])head[x]=nex[head[x]];}
    /*vector<int>st[N];
    int top(int x){return st[x].size()?*st[x].rbegin():0;}
    void push(int x,int v){st[x].push_back(v);}
    void pop(int x){while(st[x].size() && mark[*st[x].rbegin()])st[x].pop_back();}*/
}
using namespace Stack;

namespace Query
{
    struct Tquery
    {
        int v,l,r;
        Tquery(int _v=0,int _l=0,int _r=0):v(_v),l(_l),r(_r){}
    }q[N];
}
using namespace Query;

namespace Block
{
    int bi[N],bj[N],lpos[N],rpos[N];
    int x[S+5],y[S+5];
    struct Block
    {
        int pos,siz,tp,px,ps,las;
        int st[N],a[S+5],val[S+5];
        ll sum,ans[S+5];
        int &operator[](int x){return a[x];}
        int operator[](int x)const{return a[x];}
        ll resort()
        {
            ll res=0,lim=cnt>>8;
            for(int i=1;i<=siz;++i) val[i]=top(px+i),res+=(val[i]?0:a[i]);
            for(int i=0;i<=M;++i) x[i]=0;
            for(int i=1;i<=siz;++i) x[val[i]&M]++;
            for(int i=1;i<=M;++i) x[i]+=x[i-1];
            for(int i=siz;i;--i) y[x[val[i]&M]--]=val[i];
            for(int i=1;i<=lim;++i) x[i]=0;
            for(int i=1;i<=siz;++i) x[val[i]>>8]++;
            for(int i=1;i<=lim;++i) x[i]+=x[i-1];
            for(int i=siz;i;--i) val[x[y[i]>>8]--]=y[i];
            return res;
        }
        void build(){for(int i=1;i<=siz;++i)ans[0]+=a[i];sum=ans[0];}
        void rebuild()
        {
            ll trp=resort();las=st[tp];
            for(int i=siz-1;~i;--i)ans[i]=ans[i+1]+q[val[i+1]].v;ans[0]+=trp;
            ps=0;while(ps^siz && val[ps+1]<las)++ps;
            sum=ans[ps]+(ll)ps*q[las].v;
        }
        void cover(int id){st[++tp]=id;sum=(ll)siz*q[id].v;}
        void recover()
        {
            while(mark[st[tp]])--tp;int t=st[tp];
            if(t<=las)
            {
                while(ps && val[ps]>=t)--ps;
                sum=ans[ps]+(ll)ps*q[t].v;
            }
            else sum=(ll)siz*q[t].v;
        }
        ll calc(int l,int r)
        {
            int t=st[tp],v=q[t].v;ll res=0;
            if(!t){for(int i=l;i<=r;++i)res+=top(i)?q[top(i)].v:a[i-px];}
            else{for(int i=l;i<=r;++i)res+=top(i)<t?v:q[top(i)].v;}
            return res;
        }
    }b[N/S+5];
}
using namespace Block;

namespace solution
{
    ll query(int l,int r)
    {
        if(bi[l]==bi[r]) return b[bi[l]].calc(l,r);
        ll res=0;
        res+=b[bi[l]].calc(l,rpos[bi[l]]);
        res+=b[bi[r]].calc(lpos[bi[r]],r);
        for(int i=bi[l]+1;i<bi[r];++i) res+=b[i].sum;
        return res;
    }
    void update(int l,int r,int v)
    {
        if(bi[l]==bi[r]) 
        {
            for(int i=l;i<=r;++i)push(i,v);b[bi[l]].rebuild();
            return;
        }
        for(int i=l;bi[i]==bi[l];++i)push(i,v);b[bi[l]].rebuild();
        for(int i=r;bi[i]==bi[r];--i)push(i,v);b[bi[r]].rebuild();
        for(int i=bi[l]+1;i<bi[r];++i)b[i]