学术篇NOIP2017 d2t3 列队phalanx splay做法

Posted Rabbit House~❤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学术篇NOIP2017 d2t3 列队phalanx splay做法相关的知识,希望对你有一定的参考价值。

我可去他的吧....

==============先胡扯些什么的分割线==================
一道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做法的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2017 列队

luoguP3960 [noip2017]列队(树状数组)

NOIP2017列队(phalanx)解题报告

[NOIp 2017]列队

NOIP2017D2T3 列队—Treap

[NOIP2017]列队