博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【NOIP2017】列队【可持久化线段树】
阅读量:4688 次
发布时间:2019-06-09

本文共 4898 字,大约阅读时间需要 16 分钟。

  

题目描述

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

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

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

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

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

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

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

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

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

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

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

输入输出格式

输入格式:

 

输入共 q+1q+1 行。

第 1 行包含 3 个用空格分隔的正整数 n,m,qn,m,q,表示方阵大小是 nn 行 mm 列,一共发 生了 qq 次事件。

接下来 qq 行按照事件发生顺序描述了 qq 件事件。每一行是两个整数 x,yx,y,用一个空 格分隔,表示这个离队事件中离队的学生当时排在第 xx 行第 yy 列。

 

输出格式:

 

按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学 生的编号。

 

  刚拿到这道题的时候,的确是无从下手的感觉,知道有这样的一道题,【SCOI2006】动态最值,这样一道题,但是那道题是维护再一维空间上的删除并且挪动这样的一个操作,我们对于那道题只需要开线段树去维护(甚至不需要可持久化维护),维护的是size,也就是这段区间上有几个数,然后我们直接维护对应的最小值和最大值即可,线段树区间的长度就是原来的N个数的长度(因为只有N个数的说,并且还是只有删除操作)。然后一开始的时候,我们直接buildtree,并且直接赋值即可,然后在后面的找区间的时候,左区间超过L个数、右区间共有R个数,这样的(用size来维护)。

这里讲一下该怎么去维护:

我们假设左子树有X个节点,向右区间查找的时候,记得减去X,

如果ql<=X && qr>X,那么我们左右都要查;

如果ql>X && qr>X,那么我们直接查右区间;

如果ql<=X && qr<=X,我们只查左区间。

同样的,我们也可以删除。

  讲了这么多与这道题没有关系的,接下来回归到正题上来。

  思路:上面讲了这些,是为了开拓在可持久化线段树上的思路的,并且用到这道题就可以了。关键在于是怎么用?这道题会有这样的两种操作,一种是我们拎出来的人在第M列,那么他就不需要(操作1)向左移动了,直接向前移动即可;另一种是他不在第M列,此时我们就要考虑他要先向左移动,然后在向前对齐(向前对齐的时候,是不是就是可以看成我们挪动的是第M列的数)。

  有了这些思路之后,就可以开始写了,因为这里的区间长度会比较的大,很显然,我们不能去用线段树来直接维护,那么就是需要用到可持久化线段树了,先想个办法维护一下在第M列时候的情况,因为这时候我们只需要向前看齐就可以了,那么我们是不是可以看成单链的一维空间那样子的想法(就是上面的思想了),但是这里需要补,怎么补就是个问题了,我们在这里可以开一个vector<>来存,把每个最后的值就是直接pushback就可以了。

  那么,假如我们拎出去的人不是第M列的怎么办呢?假如拎出去的人的坐标是(x, y),那么我们是不是要去x这一行去找到y位的这个数,然后得到他的实际值,这时候(x, y)这个点空出来了,先要这一列的其他人都靠过来,然后再是第M列的人往前走,靠过来的做法与之前一维的时候相同,但是第M列的人向前走呢,我们可以看到M列的人,从第x位开始空出来,后面的人向前走,是不是就可以相当是第x位以后的点都向前移了一位,而查第M列的第x位不就是查其后面的数呢,然后这时候,就要把原来(x, y)上的值给赋值到M列的最后一个,也就是pushback()进去即可,同时还要把在M列x行后面的x+1行的值给塞到第x行的pushback后面去。

 

 

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #include
12 #include
13 #define lowbit(x) ( x&(-x) ) 14 #define pi 3.141592653589793 15 #define e 2.718281828459045 16 #define INF 0x3f3f3f3f 17 #define HalF (l + r)>>1 18 #define lsn rt<<1 19 #define rsn rt<<1|1 20 #define Lson lsn, l, mid 21 #define Rson rsn, mid+1, r 22 #define QL Lson, ql, qr 23 #define QR Rson, ql, qr 24 #define myself rt, l, r 25 namespace fastIO { 26 #define BUF_SIZE 100000 27 //fread -> read 28 bool IOerror = 0; 29 inline char nc() { 30 static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE; 31 if(p1 == pend) { 32 p1 = buf; 33 pend = buf + fread(buf, 1, BUF_SIZE, stdin); 34 if(pend == p1) { 35 IOerror = 1; 36 return -1; 37 } 38 } 39 return *p1++; 40 } 41 inline bool blank(char ch) { 42 return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; 43 } 44 inline void read(int &x) { 45 char ch; 46 while(blank(ch = nc())); 47 if(IOerror) return; 48 for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0'); 49 } 50 #undef BUF_SIZE 51 }; 52 using namespace fastIO; 53 using namespace std; 54 typedef unsigned long long ull; 55 typedef long long ll; 56 const int maxN = 6e5 + 7; 57 int N, M, Q, root[maxN], _UP, lc[30 * maxN], rc[30 * maxN], siz[30 * maxN], tot; 58 vector
vt[maxN]; 59 inline void insert(int &rt, int l, int r, int k) 60 { 61 if(!rt) rt = ++tot; 62 siz[rt]++; 63 if(l == r) return; 64 int mid = HalF; 65 if(k <= mid) insert(lc[rt], l, mid, k); 66 else insert(rc[rt], mid + 1, r, k); 67 } 68 int query(int rt, int l, int r, int k) 69 { 70 if(l == r) return l; 71 int mid = HalF, size_L = mid - l + 1 - siz[lc[rt]]; 72 if(k <= size_L) return query(lc[rt], l, mid, k); 73 else return query(rc[rt], mid + 1, r, k - size_L); 74 } 75 inline ll del_M_las(int x, ll val) //如果这个人是最后那一排的话,就不需要向左看齐了 76 { 77 int pos = query(root[N + 1], 1, _UP, x); insert(root[N + 1], 1, _UP, pos); 78 ll ans = pos <= N ? (ll)pos * (ll)M : vt[N + 1][pos - N - 1]; 79 vt[N + 1].push_back(val ? val : ans); 80 return ans; 81 } 82 inline ll del_N(int x, int y) 83 { 84 int pos = query(root[x], 1, _UP, y); insert(root[x], 1, _UP, pos); 85 ll ans = pos < M ? (ll)(x - 1) * (ll)M + pos : vt[x][pos - M]; 86 vt[x].push_back(del_M_las(x, ans)); 87 return ans; 88 } 89 int main() 90 { 91 tot = 0; 92 scanf("%d%d%d", &N, &M, &Q); 93 //read(N); read(M); read(Q); 94 _UP = max(N, M) + Q; 95 int x, y; 96 while(Q--) 97 { 98 scanf("%d%d", &x, &y); 99 //read(x); read(y);100 if(y == M) printf("%lld\n", del_M_las(x, 0));101 else printf("%lld\n", del_N(x, y));102 }103 return 0;104 }
View Code

 

转载于:https://www.cnblogs.com/WuliWuliiii/p/10841619.html

你可能感兴趣的文章