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+5;
const int M=1e7+2;
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+1){if(r<=n) return r-l+1;if(l<=n) return n-l+1;return 0;}if(r<m) return r-l+1;if(l<m) return m-l;return 0;
}
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+1) t[x].val=l*m;else t[x].val=(now-1)*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+1,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+1,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+1,ans=query(rt[now],1,up,x);else now=x,ans=query(rt[now],1,up,y);printf("%lld\n",ans);now=n+1;upda(rt[now],1,up,n+(++cur[now]),ans);if(y!=m){now=n+1;ans=query(rt[now],1,up,x);now=x;upda(rt[now],1,up,m-1+(++cur[now]),ans);}}return 0;
}

upda:2018.11.2

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

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

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

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

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

转载于:https://www.cnblogs.com/Miracevin/p/9582412.html

NOIP2017 列队——动态开点线段树相关推荐

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

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

  2. 【线段树合并】解题报告:luogu P4556雨天的尾巴 (树上对点差分 + 动态开点 + 线段树合并)线段树合并模板离线/在线详解

    题目链接:雨天的尾巴 本题本身是一个非常简单的一道树上差分的模板题,但是由于变态的数据范围,我们直接用数组是存不下的(本来使用一颗普通的线段树直接维护最大值即可.但是本题的空间只有128MB,直接按照 ...

  3. 动态开点线段树(多棵线段树)的内存分配与回收

    前言 线段树,是一个很好用的能支持O(logn)区间操作的数据结构,随着做一些稍微烦一点的题,有时候会发现有些情况要开一个数组的线段树,更有甚者要树套树,而在很多情况下线段树就不能把所有点都开满了(否 ...

  4. CF1045G AI robots(动态开点线段树)

    题意 火星上有$N$个机器人排成一行,第$i$个机器人的位置为$x_{i}$,视野为$r_{i}$,智商为$q_{i}$.我们认为第$i$个机器人可以看到的位置是$[x_{i}-r_{i},x_{i} ...

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

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

  6. HDU 6681 Rikka with Cake(扫描线、动态开点线段树)

    http://acm.hdu.edu.cn/showproblem.php?pid=6681 题意 在矩形区域内有k条射线,问这些射线将矩形分成了多少区域 题解 容易发现答案为所有射线交点个数+1. ...

  7. 洛谷P3120 [USACO15FEB]牛跳房子(动态开节点线段树)

    题意 题目链接 Sol \(f[i][j]\)表示前\(i\)行\(j\)列的贡献,转移的时候枚举从哪里转移而来,复杂度\(O(n^4)\) 然后考虑每一行的贡献,动态开节点线段树维护一下每种颜色的答 ...

  8. 树上启发式合并问题 ---- 2019icpc南昌 K. Tree (树上启发式合并 + 动态开点线段树)

    题目链接 题目大意: 就是给你一颗树,每个点有个权值viv_ivi​,问你有多少对(x,y)(x,y)(x,y)满足: xxx不是yyy的祖先 yyy也不是xxx的祖先 xxx和yyy的距离不超过kk ...

  9. CodeForces - 960F[动态开点线段树优化dp]详解

    题意:给一张有向图,每条边有边权与编号,求一条最长的路径,这条路径的边权与编号都是递增的.(编号指输入顺序) 首先我们回忆一下普通得LIS得做法:就是dp[i]以第i个结尾得最长上升子序列的长度,那么 ...

最新文章

  1. python字符型数据_Python基础【第五篇】:基础数据类型(字符型)
  2. 阿里云服务器如何加强服务器的安全?有哪些安全措施?
  3. js中const,var,let区别与用法
  4. math.sqrt_Math.SQRT1_2属性与JavaScript中的示例
  5. Deep Learning 【Nature review】
  6. eva每一集片尾曲是谁唱的_evaTV版的片尾曲是不是每集的都不一样啊?
  7. 安卓应用安全指南 4.6.2 处理文件 规则书
  8. 【kafka】kafka 消费报错 No entry found for connection
  9. 串口 单片机 文件_单片机引脚介绍
  10. 戴尔服务器加无线网卡用不了网,电脑安装了无线网卡却不能用是怎么回事?
  11. 串口抓包/截断工具的安装及使用详解
  12. Java中通过某一年的两个时间计算天数
  13. 2.4 PIMPLE算法 | 2.5 附加显式力的压力速度耦合(OpenFOAM理论笔记系列)
  14. mumu模拟器cpu设置_网易MuMu模拟器CPU虚拟化怎么设置?
  15. 照片怎么转换成jpg?常见渠道一览
  16. Mac上实时网速、内存等显示
  17. 电子设计竞赛(6)-逆变电路
  18. CSS实现图片自适应布局
  19. php插入语句,请教关于php中sql插入语句的问题。
  20. 微信小程序数据操作(增、删、改、查),id为随机数

热门文章

  1. 2. OD-爆破exe验证程序
  2. CEF3—在网页加载前给js对象填值
  3. Qt:Qt使用WM_COPYDATA消息进行进程通信
  4. Java知识系统回顾整理01基础03变量03字面值
  5. 在原有的基础之上,启用NAT模型
  6. 4.12L.02_CT Makefile
  7. C++ passes by reference, Java and Ruby don’t
  8. C#将图片白色背景设置为透明
  9. 首次公开,用了三年的 pandas 速查表!
  10. 深度学习高能干货:手把手教你搭建MXNet框架