1. 程式人生 > >【學術篇】NOIP2017 d2t3 列隊phalanx splay做法

【學術篇】NOIP2017 d2t3 列隊phalanx splay做法

open fin .net pda splay 表示 div getchar() std

我可去他的吧....

==============先胡扯些什麽的分割線==================
一道NOIP題我調了一晚上...(其實是因為昨晚沒有找到調試的好方法來的說...)
曾經我以為我寫完了然後全WA 0分 發現

2 1 2
1 1
1 1

這組數據能把我卡掉(我都不知道怎麽過樣例的)...
然後就開始調就精神崩潰就放棄治療就劃水就過去了一下午和一晚上...
今天我立(礫)誌要完成這道題.
上luogu打卡 兩個號(不要問我為啥兩個號)分別:
技術分享圖片
技術分享圖片
然後說我調不出WA的題我就很絕望啊 因為昨晚還有個爛攤子沒收拾呢
(其實昨晚最後精神崩潰放棄治療轉而劃水劃得太high了結果就沒調出來)

看了看d2t3的基本思路 畫了畫圖覺得可做 正好最近重拾了splay可以用splay寫一寫
(不是很清楚線段樹啊樹狀數組啊怎麽做的是吧OvO做法似乎沒有這麽顯然 動態加點線段樹我也沒寫過...)

不過昨晚把模擬寫好了 小數據調起來還是不算很費勁 但昨天認死了要調指針是導致精神崩潰的根本原因...
都動用了VS不過顯然還是不如輸出調試直觀2333
~~明明像今天一樣小數據對拍+模擬+輸出調試+面向數據差錯調個1h這不就AC了麽→_→~~
嘖嘖嘖, 其實還是感謝luogu的反向打卡加成...

=================胡扯點什麽結束的分割線=====================

然後我們來說一下做法...

30分?

想怎麽做怎麽做... 考場上不少人都水了30分暴力吧... (這就是我的模擬方法啊OvO)

50分?

詢問少據說專門處理詢問的行列就完了...但是考場上我sb地認為時間能跑過卻忘了數組開不開...GG....

80分?

考場上想到了\(O(nlog^2_2n)\)的做法...只有一行一列不是...就是把詢問數變為n*m+q(詢問個數)
然後每次查個k大的映射值就好了... 但是考場上不會寫splay... 於是就二分+樹狀數組水... 不過3e5好像要跑98M多... 然後果不其然被CCF老爺機卡掉了OvO 於是30+10滾粗...

100分?

其實我的80分思路基本是對的(其實差好多不是)OvO
我們先來看一下每次的變化...(我們查詢\((1,1)\)

)
\[ \begin{bmatrix} 1 & 2 & 3 & 4 & 5\\ 6 & 7 & 8 & 9 & 10\\ 11 & 12 & 13 & 14 & 15\\16 & 17 & 18 & 19 & 20 \end{bmatrix} => \begin{bmatrix} 2 & 3 & 4 & 5\\6 & 7 & 8 & 9 & 10\\11 & 12 & 13 & 14 & 15\\16 & 17 & 18 & 19 & 20 \end{bmatrix} => \begin{bmatrix} 2 & 3 & 4 & 5 & 10\\6 & 7 & 8 & 9 & 15\\11 & 12 & 13 & 14 & 20\\16 & 17 & 18 & 19 & 1 \end{bmatrix} \]
我們發現第m列不管你改哪都會動 這就非常麻煩 我們把它單獨提出來用一顆splay處理... 然後每行開splay維護\([1..m-1]\)
這樣時間復雜度(似乎)就夠了 不過顯然空間開不開...(尤其是開池子的人)
我們不妨就用點來表示區間 每個點存儲\([l,r]\)這個連續區間的信息...
但是操作完不就不連續了麽?
所以這就是要每行開一棵splay的原因...
當我們查詢到一個點\(p\)時, 我們拆成\([l,p-1],[p,p],[p+1,r]\)三個點...放到splay上轉就好了...
因為詢問只有\(3*10^5\)個 所以我們最多也就開120W個點嘛 還是能開開的...
這樣我們每次查詢\((x,y)\)要進行的操作就是:

  • 若y%m==0 (即查詢最後一列的點), 直接在第m+1棵(或第0棵你隨意)splay上查第x大, 然後把這個點輸出、刪除再插入...
  • 否則 在第x棵樹上查詢第y大所在的區間p 把這個區間刪除...
  • 將p拆成\([p_l,y-1] [y,y] [y+1,p_r]\) (當然這些區間中要是有\(l>r\)的當然就不要了)
  • 在第m+1棵樹上查詢第x大 這個點叫\([x,x]\)好了, 把這個點刪掉...
  • \([x,x]\)插到第x棵樹的最後, 把\([y,y]\)插到第m棵樹的最後, 把\([p_l,y-1]\)\([y+1,p_r]\)插到原來p的位置就行了...
  • 最後輸出y就行了...

然後就是一些細節問題了OvO
比如時間的先後問題 寫殘了好多遍, 最後發現n*m+q就沒有問題了...
比如查詢的時候要先算w再splay 不然會算錯(可能是我太sb了)
比如\((3*10^5)^2\)要開long long...
比如splay基本操作不要寫掛.. 就這樣吧...

代碼:

#include <cstdio>
typedef long long LL; LL n,m,q;
inline LL gn(LL a=0,char c=0){
    for(;c<48||c>57;c=getchar());for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
}

struct SPLAY{
    struct node{
        LL l,r,sz,cnt,ti;
        node *ch[2],*fa;
        void update(){sz=ch[0]->sz+ch[1]->sz+cnt;}
        int getwh(){return fa->ch[1]==this;}
        void setch(int wh,node *child);
    }*rt;
    
    void init();    
    void rotat(node *now);  
    void splay(node *now,node *tar);    
    void inser(node *nnow);
    node* find(LL k,LL &w);
    void delet(node *now);
}sp[300005];
SPLAY::node *null,pool[3000005]; LL tot;

void SPLAY::node::setch(int wh,node *child){
    ch[wh]=child; if(child!=null) child->fa=this;
    update();
}

SPLAY::node* NEW(LL l,LL r,LL t){
    if(l>r) return null;
    SPLAY::node *now=pool+ ++tot; now->ti=t;
    now->l=l; now->r=r; now->sz=now->cnt=r-l+1;
    now->ch[0]=now->ch[1]=now->fa=null;
    return now;
}

void SPLAY::rotat(node *now){
    int wh=now->getwh(); node *fa=now->fa,*fafa=fa->fa;
    if(fafa!=null) fafa->ch[fa->getwh()]=now;
    fa->setch(wh,now->ch[wh^1]);
    now->setch(wh^1,fa);
    now->fa=fafa;
}

void SPLAY::splay(node *now,node *tar){
    for(;now->fa!=tar;rotat(now))
        if(now->fa->fa!=tar)
            now->getwh()==now->fa->getwh()?rotat(now->fa):rotat(now);
    if(tar==null) rt=now;
}

void SPLAY::inser(node *nnow){
    node *fa=null,*now=rt;
    while(now!=null){
        fa=now;
        if(nnow->ti<now->ti) now=now->ch[0];
        else now=now->ch[1];
    }
    if(fa==now) rt=nnow;
    else if(nnow->ti<fa->ti) fa->setch(0,nnow);
    else fa->setch(1,nnow);
    splay(nnow,null);
}

SPLAY::node* SPLAY::find(LL k,LL &w){
    node *now=rt; LL ls=0;
    while(now!=null){
        if(ls+now->ch[0]->sz<k&&ls+now->ch[0]->sz+now->cnt>=k){
            w=k-(ls+now->ch[0]->sz)+now->l-1;
            splay(now,null);        
            return now;
        }
        if(ls+now->ch[0]->sz>=k) now=now->ch[0];
        else ls+=now->ch[0]->sz+now->cnt,now=now->ch[1];
    }
    return null;
}

void SPLAY::delet(node *now){
    if(now->ch[0]==null&&now->ch[1]==null) rt=null;
    else if(now->ch[0]==null) rt=now->ch[1],now->ch[1]->fa=null;
    else if(now->ch[1]==null) rt=now->ch[0],now->ch[0]->fa=null;
    else{
        node *rs=now->ch[0];
        while(rs->ch[1]!=null) rs=rs->ch[1];
        splay(rs,null);
        rs->setch(1,now->ch[1]);
        rt=rs; rs->fa=null;
    }
}

void init(){
    null=pool; null->l=null->r=null->sz=null->cnt=0;
    null->ch[0]=null->ch[1]=null->fa=null;
    for(int i=0;i<=n;++i) sp[i].rt=null;
}

LL query(int q,int x,int y){
    if(y%m==0){
        LL p=0; SPLAY::node *now=sp[0].find(x,p);
        p=now->l;
        sp[0].delet(now);
        sp[0].inser(NEW(p,p,n*m+q));
        return p;
    }
    else{
        LL p=0,p2=0;
        SPLAY::node *now=sp[x].find(y,p);
//      printf("A%d %d %d\n",p,now->l,now->r);
        SPLAY::node *now2=sp[0].find(x,p2);     
//      printf("B%d %d %d\n",p2,now2->l,now2->r);
        p2=now2->l;
        sp[x].delet(now);
        sp[0].delet(now2);
        if(p>now->l) sp[x].inser(NEW(now->l,p-1,now->l));
        if(p<now->r) sp[x].inser(NEW(p+1,now->r,p+1));
        sp[x].inser(NEW(p2,p2,n*m+q));
        sp[0].inser(NEW(p,p,n*m+q));
        return p;
    }
    return 0;
}

void debugtree(SPLAY::node *now){
    if(now->ch[0]!=null) debugtree(now->ch[0]);
    printf("%d %d\n",now->l,now->r);
    if(now->ch[1]!=null) debugtree(now->ch[1]);
}

void solve(){
    for(LL i=1;i<=n;++i)
        sp[i].inser(NEW((i-1)*m+1,i*m-1,(i-1)*m+1));
    for(LL i=1;i<=n;++i)
        sp[0].inser(NEW(i*m,i*m,i*m));
    for(LL i=1,x,y;i<=q;++i){
        x=gn(),y=gn();// query(x,y);
        printf("%lld\n",query(i,x,y));
//  puts("```"); 
//  for(int i=1;i<=n;++i)
//      debugtree(sp[i].rt),putchar(10);
//  debugtree(sp[0].rt);
//  puts("```");
    }   
}
int main(){
//  freopen("phalanx.in","r",stdin); freopen("phalanx.out","w",stdout);
    n=gn(),m=gn(),q=gn(); init(); solve();
}

其實最後一個點在luogu上就跑了1800+ms 平衡樹的常數是真的大(而且我可能寫的醜什麽的常數就更大了)
所以其實打卡說的沒錯 放到NOIP老爺機上估計就變成TLE了...
然而我實在懶得去卡常數了 就這樣吧...

【學術篇】NOIP2017 d2t3 列隊phalanx splay做法