|
|
## $P2253$ 好一个一中腰鼓!
|
|
|
|
|
|
### 一、题目背景
|
|
|
话说我大一中的运动会就要来了,据本班同学剧透(其实早就知道了),我萌萌的初二年将要表演腰鼓[喷],这个无厘头的题目便由此而来。
|
|
|
|
|
|
$Ivan$乱入:“忽一人大呼:‘好一个安塞腰鼓!’满座寂然,无敢哗者,遂与外人间隔。”
|
|
|
|
|
|
### 二、题目描述
|
|
|
设想一下,腰鼓有两面,一面是红色的,一面是白色的。初二的苏大学神想给你这个$oier$出一道题。假设一共有$N$($1<=N<=20,000$)个同学表演,表演刚开始每一个鼓都是红色面朝向观众,舞蹈老师会发出$M$($1<=M<=20,000$)个指令,如果指令发给第$i$个表演的同学,这位同学就会把腰鼓反过来,如果腰鼓之前是红色面朝向观众的,那么就会变成白色面朝向观众,反之亦然。那么问题来了(!?),在老师每一次发出指令后,**找到最长的连续的一排同学**,满足每相邻的两个手中的腰鼓朝向观众的一面互不相同,输出这样一排连续的同学的人数。
|
|
|
|
|
|
#### 输入输出格式
|
|
|
输入格式:
|
|
|
|
|
|
第一行有两个整数, 分别为表演的同学总数$N$, 和指令总数$M$。
|
|
|
|
|
|
之后$M$行, 每行有一个整数$i$: $1<=i<=N$, 表示舞蹈老师发出的指令。
|
|
|
|
|
|
#### 输出格式:
|
|
|
输出有$M$行, 其中每$i$行有一个整数.
|
|
|
|
|
|
表示老师的第$i$条指令发出之后, 可以找到的满足要求的最长连续的一排表演同学有多长?
|
|
|
|
|
|
#### 输入输出样例
|
|
|
输入样例#1:
|
|
|
```cpp {.line-numbers}
|
|
|
6 2
|
|
|
2
|
|
|
4
|
|
|
```
|
|
|
输出样例#1:
|
|
|
```cpp {.line-numbers}
|
|
|
3
|
|
|
5
|
|
|
```
|
|
|
说明
|
|
|
$Huangc$温馨提示:其实数据根本没你想象的那么大。。。[坏笑]、、
|
|
|
|
|
|
|
|
|
### 三、解题思路
|
|
|
分析:一类线段树的经典题型,这种题和 **求最长相同颜色区间的做法** 是差不多的,都是要用线段树解决,那么要记录哪些信息呢?满足要求的区间长度是肯定要记录的,还要记录每一个区间左端点和右端点的颜色,仅仅用这些信息还不能更新区间长度,还要记录每个区间从左向右最多能扩展多少,从右向左能扩展多少,这样就能求出答案来。
|
|
|
|
|
|
合并是一个难点,当前区间的答案可能是左区间的答案也可能是右区间的答案也可能是左右中间合并的答案,在合并的时候判断一下左右端点的颜色是否相同就好了.同时还要更新向右延伸的最大长度和向左延伸的最大长度.
|
|
|
|
|
|
### 四、实现代码
|
|
|
```cpp {.line-numbers}
|
|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
const int N = 20010;
|
|
|
|
|
|
// 线段树
|
|
|
#define mid ((l + r) >> 1)
|
|
|
#define ls (u << 1)
|
|
|
#define rs (u << 1 | 1)
|
|
|
struct Node {
|
|
|
int l, r, len;
|
|
|
int lx, rx, mx; // lx:左起最长符合条件的长度;rx:右起最长符合条件的长度;mx:整体区间内符合条件的长度
|
|
|
} tr[N << 2];
|
|
|
|
|
|
int color[N]; // 每个位置上的颜色
|
|
|
|
|
|
void pushup(int u) {
|
|
|
// 左儿子的最右端点颜色,与右儿子的最左端点颜色是否相同
|
|
|
bool flag = color[tr[ls].r] != color[tr[rs].l];
|
|
|
|
|
|
tr[u].lx = tr[ls].lx; // 左起最长,继承左儿子的左起最长
|
|
|
// 如果左儿子全部区间满足条件,并且,左儿子最右,不等于,右儿子最左,也就是继续符合条件
|
|
|
if ((tr[ls].lx == tr[ls].len) && flag) tr[u].lx = tr[ls].len + tr[rs].lx;
|
|
|
|
|
|
tr[u].rx = tr[rs].rx; // 右起最长,继承右儿子的右起最长
|
|
|
// 如果右儿子全部区间满足条件,并且,右儿子最左,不等于,左儿子最右,也就是继续符合条件
|
|
|
if ((tr[rs].rx == tr[rs].len) && flag) tr[u].rx = tr[rs].len + tr[ls].rx;
|
|
|
|
|
|
tr[u].mx = max(tr[ls].mx, tr[rs].mx);
|
|
|
if (flag) tr[u].mx = max(tr[u].mx, tr[ls].rx + tr[rs].lx); // 拼接
|
|
|
}
|
|
|
|
|
|
void build(int u, int l, int r) {
|
|
|
tr[u].l = l, tr[u].r = r, tr[u].len = r - l + 1;
|
|
|
|
|
|
if (l == r) {
|
|
|
tr[u].lx = tr[u].rx = tr[u].mx = 1; // 表演刚开始每一个鼓都是红色面朝向观众
|
|
|
return;
|
|
|
}
|
|
|
build(ls, l, mid);
|
|
|
build(rs, mid + 1, r);
|
|
|
pushup(u);
|
|
|
}
|
|
|
|
|
|
void modify(int u, int p) {
|
|
|
int l = tr[u].l, r = tr[u].r;
|
|
|
if (l == r) {
|
|
|
color[p] = !color[p]; // 红变白,白变红
|
|
|
return;
|
|
|
}
|
|
|
if (p <= mid)
|
|
|
modify(ls, p);
|
|
|
else
|
|
|
modify(rs, p);
|
|
|
pushup(u);
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
#ifndef ONLINE_JUDGE
|
|
|
freopen("P2253.in", "r", stdin);
|
|
|
#endif
|
|
|
int n, m;
|
|
|
cin >> n >> m;
|
|
|
|
|
|
build(1, 1, n);
|
|
|
|
|
|
while (m--) {
|
|
|
int p;
|
|
|
cin >> p;
|
|
|
modify(1, p);
|
|
|
printf("%d\n", tr[1].mx);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
``` |