【学术篇】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了...
然而我实在懒得去卡常数了 就这样吧...

原文地址:https://www.cnblogs.com/enzymii/p/8412194.html

时间: 2024-07-30 22:14:39

【学术篇】NOIP2017 d2t3 列队phalanx splay做法的相关文章

【NOIP2017】列队(Splay)

[NOIP2017]列队(Splay) 题面 洛谷 题解 其实好简单啊... 对于每一行维护一棵\(Splay\) 对于最后一列维护一棵\(Splay\) \(Splay\)上一个节点表示一段区间 每次出去一个人就是把当前的\(Splay\)的一个节点拆分成\(3\)个 然后就很简单了.. 细节比较多.. #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #incl

[luogu P3960] [noip2017 d2t3] 队列

[luogu P3960] [noip2017 d2t3] 队列 题目描述 Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n \times mn×m名学生,方阵的行数为 nn,列数为 mm. 为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 1 到 n \times mn×m 编上了号码(参见后面的样例).即:初始时,第 ii 行第 jj 列 的学生的编号是(i-1)

【HNOI2002】营业额统计【重做-Splay做法】

之前用sbt写了这个题现在自己开发了一套Splay的模板继续拿这道题试试手 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define MAXINT 0x7fffffff using namespace std; struct splay { splay *ch[2],*fa;//ch[0]为左子树ch[1]为右

【HNOI2004】宠物收养所【重做Splay做法】

Splay真心比sbt难调多了QAQ 调一个remove一上午终于搞好了QAQ #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define P 1000000 #define MAXINT 100000000000 using namespace std; struct splay { splay *ch[2],

【学术篇】一些水的不行的dp

最近做了几道非常水非常水的dp...... 之后刷的一些水dp也会写在这里...... 此篇题目难度不递增!!! Emmmm....... 1.luogu1043数字游戏 以前看过这个题几遍,没做这个题(觉得太难了)...... 就是一道环形的区间dp......本来想按常规的思路拆成2n做,但是换算坐标好麻烦, 然后一看n,m好小于是就直接强行把数组错位做了...... 设\(f[i][j]\)为前i个点分j份能得到的最大值,\(g[i][j]\)为前i个分j份能得到的最小值, 用\(mod(

【学术篇】SDOI2011 计算器

好一道三合一...(然而被我做成了四合一) 其实1 2 3是独立的OvO 然后就可以逐个分析了... 1 快速幂..就不说了..(我省选的时候有这么水的20pts部分分么←_← 2 两种做法(写在标题里面了).. 2.1(扩展欧几里得) \(xy\equiv z(mod\ p)\) 很显然可以写成\(xy=np+z\), 移项得\(xy-np=z\) 为保证\(y,p\)互质, 我们让两边同除\(gcd(y,p)\), 也就是让\(y,p,z\)分别除\(gcd(y,p)\) 由扩展欧几里得,

【学术篇】分析矿洞 杜教筛

数论什么的都去死吧! 看着题解我都能化式子用完4页草纸... 另外吐槽一句出题人的拼音学的是真好, 不知道是不是故意的. 其实题解已经写得挺详细的了. 我就是提一些出题人觉得太easy没必要提但是做题还是需要的一些东西....(因为这些东西我基本都是现学的) 然而之前刚刚学完mobius反演就暂时性脱坑的我啥也不会啊.. 看到前排dp和曲神在水luogu的欢(bao/du)乐(ling/liu)赛, 就想去看看. 然后就点了报名但是发现自己什么都不会. 去看了看T1. 就是这道题. 然后成功的化

【学术篇】bzoj2440 [中山市选2011]完全平方数

-题目の传送门- 题目大意: 找到第k个无平方因子数. 看到数据范围很大, 我们要采用比\(O(n)\)还要小的做法. 考虑如果前\(x\)个数中有\(k-1\)个无平方因子数, 而前\(x+1\)个数中有\(k\)个无平方因子数, 那么\(x\)即为所求. 而由某种我不会的方式可以证明出答案是不会超过\(2n\)的, 所以我们可以二分答案. 问题就转化成了求前\(x\)个数中有多少个无平方因子数. 所以我们就要把这些无平方因子数筛一下. 那么首先所有含有平方因子的数我们都不需要考虑, 因为它们

【学术篇】SPOJ QTREE 树链剖分

发现链剖这东西好久不写想一遍写对是有难度的.. 果然是熟能生巧吧.. WC的dalao们都回来了 然后就用WC的毒瘤题荼毒了我们一波, 本来想打个T1 44分暴力 然后好像是特判写挂了还是怎么的就只能得28pts.. 重新见到这些失踪的dalao灰常开心, 于是想让自己心情稍微差一点, 就想着把自己昨天写WA的QTREE重构一遍吧.. 于是重构的sb链剖果然挂掉了... 出现了各种各样的漏洞... 忘记各种各样的句子, 然而退化成了暴力小数据也随便过看不出来啊~~~ 但是还是在1h之内调对了_(