Description:

Sylvia 是一个热爱学习的女♂孩子。

前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。

Sylvia 所在的方阵中有n×m名学生,方阵的行数为 n,列数为 m。

为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 1 到 n×m 编上了号码(参见后面的样例)。即:初始时,第 i 行第 j 列 的学生的编号是(i−1)×m+j。

然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了 q件这样的离队事件。每一次离队事件可以用数对(x,y)(1≤x≤n,1≤y≤m)描述,表示第 x 行第 y 列的学生离队。

在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:

  1. 向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条 指令之后,空位在第 x 行第 m 列。

  2. 向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第 n 行第 m 列。

教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 n 行 第 m 列一个空位,这时这个学生会自然地填补到这个位置。

因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学 的编号是多少。

注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后 方阵中同学的编号可能是乱序的。

Hint

Solution

一年前暴力敲了30pts

一年后暴力敲了60pts

没什么长进啊

还是不会正解。

1.不懂树状数组

2.不想写平衡树

所以我们写动态开点线段树

首先发现,对于x=1的点,可以想到对这个链开一棵长度为max(n,m)+q的线段树。每次找第k个有数的地方,然后放到最后的位置。

发现,每次向前对齐只有最后一列要动,

向左看齐,只是当前的行会向左移动。

所以,为了便于操作,我们开n+1棵线段树,前n棵维护i行,1~m-1的答案

最后一棵n+1,维护最后一列n个答案。

然后我们就得到了一个优秀的MLE做法辣!~~

所以就要动态开点线段树。

(因为我比较弱)所以简单讲解一下动态开点线段树。

发现,有的时候,线段树需要维护的区间很大很大,但是实际用到的节点很少很少。

那么,我们干脆就不要开这么多的节点,用到的时候再向内存要。

也就是说,我们建立了一棵残疾的线段树,缺少很多枝叶,但是绝对够用了。

画个图大概理解一下(虽然也不太对)

实心边框的点都是我们申请内存给的,虚的点是没用的。就算申请也不用,实在是浪费资源。

所以,

我们开局只有一个根,装备叶子全靠给。

例如我们要建立一个权值线段树,但是在线操作不让你离散化,值域又是inf级别的,

像这样,即使这个区间的范围很大,但是如果询问q比较少的话,我们只需要qloginf个节点,就可以办到。

(发现和主席树有点像,但是省空间的思想还是有些不同的。)

然后我们用动态开点线段树来做这个题。

线段树根节点维护的区间是max(n,m)+q;

开始每个线段树甚至连根也不用建,需要的时候会建起来。

每个线段树节点记录sz,子树实际的人数大小。(开始的时候,只有1~n(m-1)是sz=r-l+1的)

sz可以用一个函数处理。虽然并没有这么多的叶子,但是实际上,建出这么多的叶子,也是这个sz(这也是能动态开点的条件)

再记录一个val(long long型需注意),记录当前节点所代表的人的编号

这个编号val只有在叶子节点才有用。

其实每次询问引起的变化是:树x的第y个人走了,进入了树n+1的末尾,树n+1的第x走了,进入树x的末尾。

每次询问,如果y==m就进入线段树n+1查询,否则进入线段树x查询,找到答案ans输出

查询的时候,顺便sz--,删掉途经点的sz(就不用pushup了)

把ans这个编号放进n+1线段树的末尾(新开一个位置)

同样,途经sz++

如果y!=m说明,第x棵线段树最后进来一个人。就把n+1的第x个人查询(删除),放进线段树x的末尾(新开一个位置)。

这样子,其实每棵线段树根节点的sz都保持为m-1(或n)

Code

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
const int N=3e5+;
const int M=1e7+;
ll n,m,q;
struct node{
int ls,rs;
int sz;
ll val;
}t[M];
int id,tot;
int rt[N];
ll now;
int cur[N];
int up;
int get(int l,int r){
if(now==n+){
if(r<=n) return r-l+;
if(l<=n) return n-l+;
return ;
}
if(r<m) return r-l+;
if(l<m) return m-l;
return ;
}
ll query(int &x,int l,int r,int c){
if(!x){
x=++tot;
t[x].sz=get(l,r);
if(l==r){
if(now==n+) t[x].val=l*m;
else t[x].val=(now-)*m+l;
}
}
t[x].sz--;
if(l==r) return t[x].val;
if((!t[x].ls&&c<=get(l,mid))||c<=t[t[x].ls].sz) return query(t[x].ls,l,mid,c);
else{
if(!t[x].ls) c-=get(l,mid);
else c-=t[t[x].ls].sz;
return query(t[x].rs,mid+,r,c);
}
}
void upda(int &x,int l,int r,int to,ll d){
if(!x){
x=++tot;
t[x].sz=get(l,r);
if(l==r){
t[x].val=d;
}
}
t[x].sz++;
if(l==r) return;
if(to<=mid) return upda(t[x].ls,l,mid,to,d);
else return upda(t[x].rs,mid+,r,to,d);
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&q);
int x,y;
ll ans;
up=max(n,m)+q;
while(q--){
scanf("%d%d",&x,&y);
if(y==m) now=n+,ans=query(rt[now],,up,x);
else now=x,ans=query(rt[now],,up,y);
printf("%lld\n",ans); now=n+;
upda(rt[now],,up,n+(++cur[now]),ans);
if(y!=m){
now=n+;
ans=query(rt[now],,up,x);
now=x;
upda(rt[now],,up,m-+(++cur[now]),ans);
}
}
return ;
}

upda:2018.11.2

感觉这个动态开点线段树其实不算是典型的动态开点23333

一般的线段树都是区间表示连续一些下标之类。动态开点也是如此。

但是这个做法的话,愣是把线段树写成了平衡树的存储方式。

区间的长度仅仅代表的是预留空间。

就是把许多点压成了一个点。

NOIP2017 列队——动态开点线段树的更多相关文章

  1. Luogu P3960 列队(动态开点线段树)

    P3960 列队 题意 题目描述 Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia所在的方阵中有\(n \times m ...

  2. 洛谷P3960 列队(动态开节点线段树)

    题意 题目链接 Sol 看不懂splay..,看不懂树状数组... 只会暴力动态开节点线段树 观察之后不难发现,我们对于行和列需要支持的操作都是相同的:找到第\(k\)大的元素并删除,在末尾插入一个元 ...

  3. [2016湖南长沙培训Day4][前鬼后鬼的守护 chen] (动态开点线段树+中位数 or 动规 or 贪心+堆优化)

    题目大意 给定一个长度为n的正整数序列,令修改一个数的代价为修改前后两个数的绝对值之差,求用最小代价将序列转换为不减序列. 其中,n满足小于500000,序列中的正整数小于10^9 题解(引自mzx神 ...

  4. [bzoj 3531][SDOI2014]旅行(树链剖分+动态开点线段树)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3531 分析: 对于每个颜色(颜色<=10^5)都建立一颗线段树 什么!那么不是M ...

  5. 【BZOJ-4636】蒟蒻的数列 动态开点线段树 ||(离散化) + 标记永久化

    4636: 蒟蒻的数列 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 247  Solved: 113[Submit][Status][Discuss ...

  6. codeforces 893F - Physical Education Lessons 动态开点线段树合并

    https://codeforces.com/contest/893/problem/F 题意: 给一个有根树, 多次查询,每次查询对于$x$i点的子树中,距离$x$小于等于$k$的所有点中权值最小的 ...

  7. codeforces 915E - Physical Education Lessons 动态开点线段树

    题意: 最大$10^9$的区间, $3*10^5$次区间修改,每次操作后求整个区间的和 题解: 裸的动态开点线段树,计算清楚数据范围是关键... 经过尝试 $2*10^7$会$MLE$ $10^7$会 ...

  8. CF915E Physical Education Lessons 动态开点线段树

    题目链接 CF915E Physical Education Lessons 题解 动态开点线段树 代码 /* 动态开点线段树 */ #include<cstdio> #include&l ...

  9. 洛谷P3313 [SDOI2014]旅行(树链剖分 动态开节点线段树)

    题意 题目链接 Sol 树链剖分板子 + 动态开节点线段树板子 #include<bits/stdc++.h> #define Pair pair<int, int> #def ...

随机推荐

  1. WPF save listbox config

    UI <Grid x:Class="WzlyTool.ReplyContentUI" xmlns="http://schemas.microsoft.com/win ...

  2. 20155229《网络对抗技术》Exp3:免杀原理与实践

    实验预习 免杀: 看为一种能使病毒木马避免被杀毒软件查杀的技术. 免杀的分类: 开源免杀:指在有病毒.木马源代码的前提下,通过修改源代码进行免杀.. 手工免杀:指在仅有病毒.木马的可执行文件(.exe ...

  3. Hiberante持久化对象的3种状态

        近日一同事问我关于Hibernate中对象的3种状态的问题,因此特意总结一下.在Hibernate中对象是有以下3中状态: 瞬时态(transient object): 没有OID值,没有被s ...

  4. WPF编程,窗口保持上次关闭时的大小与位置。

    原文:WPF编程,窗口保持上次关闭时的大小与位置. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/details/8 ...

  5. 【Java框架型项目从入门到装逼】第十一节 用户新增之把数据传递到后台

    让我们继续来做"主线任务",这一节,我们来做具体的用户新增功能.首先,为了简单起见,我把主页面改了一些,改的是列表那一块.删去了一些字段,和数据库表对应一致: 现在,我们要实现一个 ...

  6. stm32f051 DMA需要注意的一点

    STM32f051的DMA注意事项 问题说明:在使用ADC的DMA通道时,遇到了序列转换的乱序问题,我使用的是DMA循环模式,但是采集的数据却总是错的:第二个内存地址存放的是ADC序列转换中的第一个通 ...

  7. libgdx学习记录25——Rectangle与Circle是否重叠

    Rect与Circle重叠有三种情况: 1. Rect至少有一个角在Circle里面 2. Circle与Rect的左边或右边相交,或者Circle在Rect内 3. Circle与Rect的顶边或底 ...

  8. Spring Boot(七):Mybatis 多数据源最简解决方案

    说起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务.我们遇到的情况是后者,网上找了很多,大都是根据 Jpa 来做多数据源解决方案,要不就是老的 Spring 多 ...

  9. Scrapy框架中的CrawlSpider

    小思考:如果想要通过爬虫程序去爬取”糗百“全站数据新闻数据的话,有几种实现方法? 方法一:基于Scrapy框架中的Spider的递归爬取进行实现(Request模块递归回调parse方法). 方法二: ...

  10. 2019大疆PC软件开发笔试——开关和灯泡两个电路板

    题目描述: 小A是一名DIY爱好者,经常制作一些有趣的东西. 今天,小A突然想要来做这样一个东西.小A现在有两块同样大小为n×m,有n×m块大小为1×1小电路板拼成的矩形电路板,假设叫做电路板A和电路 ...