[Ynoi2015]我回來了

在太陽西斜的這個世界裏,置身天上之森。
等這場戰爭結束後,不歸之人與望眼欲穿的人們,人人本着正義之名。
長存不滅的過去,逐漸消逝的未來。
我 回 來 了
縱使日薄西山,即便看不到未來,
此時此刻的光輝,盼君勿忘。
——世上最幸福的女孩

題面說的很繞,第22個操作,說了一堆期望什麼的,最後發現要乘上(RL+1)(R-L+1),所以就是求情況之和嘛

我們考慮褻瀆什麼時候可以被觸發

首先他一定會觸發一次,那麼這個時候需要讓場上所有人的血量d-d,想要觸發下一次,一定需要有一個人的血量在[1,d][1,d]的範圍內,如果觸發第二次呢?不難發現就是需要有一個人的血量在[d+1,2d][d+1,2d]的範圍內,以此類推

所以對於傷害值爲dd的情況,褻瀆能夠被觸發的次數就是最大的kk,使得1i<k,hj[i×dd+1,i×d]\forall 1\leq i< k,\exists h_j\in[i\times d-d+1,i\times d]

因爲每次只有插入一個數,所以我們發現kdk_d一定會逐漸變大

考慮變化的量,應該是i=1nni\sum_{i=1}^n \lceil\frac{n}{i}\rceil左右,算出來大概是1.2×1061.2\times 10^6,可以證明這個東西<nlogn<n\log n,也就是說最多隻會變化nlognn\log n

那麼我們可以形成一個基本的思路就是每次插入一個數的時候,快速的找到需要更改的位置,然後暴力更改,後面那個東西可以利用一個樹狀數組進行維護

考慮快速插入的時候怎麼找到需要更改的位置

我們發現,當傷害值爲dd,當前次數爲tt的時候,我們只有插入的一個在[d×td+1,d×t][d\times t-d+1,d\times t]裏面的數的時候,才能夠向後擴展,所以我們可以儲存下來每一個dd現在的目標區間,就是[d×td+1,d×t][d\times t-d+1,d\times t],然後插入xx的時候找到所有包含xx的區間暴力更新,更新之後插入新的目標區間


因爲最多隻會被查找nlognn\log n次,我們嘗試每次用logn\log n的時間查找一個滿足條件的區間,那麼這個時候複雜度是O(nlog2n)O(n\log^2n),是基本可以接受的

那麼問題就轉化成了每次求包含一個點xx的每一個滿足條件的區間

我們可以使用線段樹+set的方法來解決

我們建立nn個set,set[i]set[i]儲存的是所有左端點爲ii的區間的右端點的值

再建立一棵線段樹,每個節點儲存左端點位於子區間中的區間的右端點的最大值,線段樹的每個葉子節點就相應的對應每一個set

對於一個xx,每次我們不停地查找線段樹上的[1,x][1,x]這一段區間,每次查詢出來的就是一個滿足條件的,直到[1,x][1,x]這一段區間的最大右端點<x<x,就一定沒有滿足條件的區間了,停止查詢

怕被卡常,所以這裏我選擇了手寫平衡樹


但是這樣的寫法還有一些細節

比如說我們每次真的只會往後擴展一次嗎?

比如說先插入了一個66,對於d=3d=3來說,不能更新

接下來插入一個22,這個時候d=3d=3不是隻更新一次,因爲殺掉22之後還可以殺掉66,這個時候會更新兩次

因爲我們只儲存了每個dd當前的目標區間,還可能後面有但是當時不能更新的,所以我們需要再開一個樹狀數組來儲存小兵的情況,每次拓展多次

這一段的代碼:

        if(opt==1){
            bit2.add(x,1);//x位置多了一個人
            while(1){
                node now=seg.query(1,1,n,1,x);//查找當前l在[1,x]中r最大的區間
                if(now.max<x)break;//如果比x小,就沒有了,結束循環
                treap.del(now.maxid,now.max),seg.update(1,1,n,now.maxid);//在平衡樹中刪去這一條線段,同時線段樹的信息需要更新
                int bel=now.max-now.maxid+1,l=now.maxid,r=now.max;//r-l+1就是這個點的d,嘗試向右拓展
                while(bit2.ask(r)-bit2.ask(l-1)>0&&l<=n)//如果這個區間有小兵
                    l+=bel,r+=bel,bit1.add(bel,1);//更新答案
                if(l<=n)treap.ins(l,r),seg.update(1,1,n,l);//如果還在[1,n]裏面就插入新的目標線段
            }
        }

另外樹狀數組的代碼也需要相應修改,因爲我們可能查找到一段的ln,r>nl\leq n,r>n,這個同樣是合法的,但是查詢的時候可能會出錯,因爲bit[r]=0bit[r]=0,所以樹狀數組查詢時,如果o>no>no=no=n即可

空間複雜度寫回收的話O(n)O(n),時間複雜度大約O(nlog2n)O(n\log^2n)

要寫兩棵樹狀數組+一棵線段樹+一棵平衡樹,我選擇了結構體封裝的版本,同時表達信仰啊qwq

chtholly->維護答案的樹狀數組
almeria->維護小兵的樹狀數組
ithea->維護區間的平衡樹
nephren->線段樹

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

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;

const int N=1e5+5;

template<typename T> void read(T &x){
   x=0;int f=1;
   char c=getchar();
   for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
   for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}

int n,m;
int son[N*10][2],siz[N*10],val[N*10],treap[N*10];
int bin[N*10],binsiz;
int root[N],tot;

struct node{
    int maxid,max;
}seg[N<<2];

struct chtholly_almeria{
    int bit[N];
    int lowbit(int o){
        return o&-o;
    }
    void add(int o,int x){
        for(;o<=n;o+=lowbit(o))bit[o]+=x;
    }
    int ask(int o){
    	if(o>n)o=n;
        int res=0;
        for(;o;o-=lowbit(o))res+=bit[o];
        return res;
    }
}Chtholly,Almeria;

struct ithea{
    int newnode(){
        if(!binsiz)return ++tot;
        else return bin[binsiz--];
    }
    void delnode(int x){
        bin[++binsiz]=x;
        siz[x]=val[x]=treap[x]=son[x][0]=son[x][1]=0;
    }
    void update(int x){
        siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
    }
    int merge(int u,int v){
        if(!u||!v)return u|v;
        int rt;
        if(treap[u]<treap[v])son[rt=u][1]=merge(son[u][1],v);
        else son[rt=v][0]=merge(u,son[v][0]);
        return update(rt),rt;
    }
    void split(int o,int &u,int &v,int k){
        if(!o){u=v=0;return;}
        if(val[o]<=k)split(son[u=o][1],son[o][1],v,k);
        else split(son[v=o][0],u,son[o][0],k);
        update(o);
    }
    void ins(int l,int r){
        int lft,rht;
        split(root[l],lft,rht,r);
        int u=newnode();
        treap[u]=rand(),siz[u]=1,val[u]=r;
        root[l]=merge(merge(lft,u),rht);
    }
    void del(int l,int r){
        int lft,mid,rht;
        split(root[l],lft,rht,r);
        split(lft,lft,mid,r-1);
        root[l]=merge(lft,rht);
        delnode(mid);
    }
    int max(int l){
        int u=root[l];
        while(son[u][1])u=son[u][1];
        return val[u];
    }
}Ithea;

struct nephren{
    # define lc (u<<1)
    # define rc (u<<1|1)
    node merge(node l,node r){
        if(l.max>r.max)return l;
        else return r;
    }
    void update(int u,int l,int r,int x){
        if(l==r){
            if(root[l])seg[u].max=Ithea.max(l),seg[u].maxid=l;
            else seg[u].max=seg[u].maxid=0;
            return;
        }   
        int mid=l+r>>1;
        if(x<=mid)update(lc,l,mid,x);
        else update(rc,mid+1,r,x);
        seg[u]=merge(seg[lc],seg[rc]);
    }
    node query(int u,int l,int r,int ql,int qr){
        if(l>=ql&&r<=qr)return seg[u];
        int mid=l+r>>1;
        if(qr<=mid)return query(lc,l,mid,ql,qr);
        if(ql>mid)return query(rc,mid+1,r,ql,qr);
        return merge(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
    }
}Nephren;

int main()
{
    // freopen("testdata.in","r",stdin);
    srand(19260817);
    read(n),read(m);
    Rep(i,1,n){
        Chtholly.add(i,1);
        Ithea.ins(1,i),Nephren.update(1,1,n,1);
    }
    Rep(i,1,m){
        int opt,x,y;
        read(opt),read(x);
        if(opt==1){
            Almeria.add(x,1);
            while(1){
                node now=Nephren.query(1,1,n,1,x);
                if(now.max<x)break;
                Ithea.del(now.maxid,now.max),Nephren.update(1,1,n,now.maxid);
                int bel=now.max-now.maxid+1,l=now.maxid,r=now.max;
                while(Almeria.ask(r)-Almeria.ask(l-1)>0&&l<=n)
                    l+=bel,r+=bel,Chtholly.add(bel,1);
                if(l<=n)Ithea.ins(l,r),Nephren.update(1,1,n,l);
            }
        }
        else read(y),printf("%d\n",Chtholly.ask(y)-Chtholly.ask(x-1));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章