T2 patrick
(WOJ4760)
2.1 题目描述
派大星的家门前有一条河(请不要向出题人提问海底为什么会有河),派大星每天要观察这条河,并且统计河中岛屿的个数。河床的地形可以抽象为一个长度为
n
n
n的数列
{
a
i
}
\lbrace ai \rbrace
{ai},第i 位的数字代表河床对应位置的高度。当水位为
h
h
h时,所有高度低于
h
h
h的位置都会被水覆盖,高度大于等于
h
h
h的地形就露出水面,连成了岛屿。比如当
{
a
i
}
=
{
5
,
3
,
2
,
6
,
1
}
\lbrace ai\rbrace=\lbrace 5,3,2,6,1\rbrace
{ai}={5,3,2,6,1},且
h
=
3
h= 3
h=3时,河中共有
2
2
2个岛屿,分别为
{
5
,
3
}
\lbrace 5,3\rbrace
{5,3}和
{
6
}
\lbrace 6\rbrace
{6}。
请你协助派大星统计每一天的岛屿的个数。河床每个位置的高度可能会发生变化,而且水位也会发生变化。
2.2 输入描述
输入文件为patrick.in。
第一行两个正整数
N
,
M
N,M
N,M,表示数列的长度,和询问的个数。
第二行N个正整数
H
i
H_i
Hi,表示一开始的数列,即一开始河床在每处的高度。
接下来
M
M
M行,每行有如下两种可能的格式:
- Q Q Q外加一个整数 C C C,表示询问若将水位抬高或降低到 C ⨁ L a s t C\bigoplus Last C⨁Last,湖中会有几个岛。
-
C
C
C外加两个整数
A
,
B
A,B
A,B,表示
A
⨁
L
a
s
t
A\bigoplus Last
A⨁Last处的高度变为了
B
⨁
L
a
s
t
B\bigoplus Last
B⨁Last。其中
L
a
s
t
Last
Last表示上一次询问(
Q
Q
Q操作)的答案。
L
a
s
t
Last
Last一开始等于
0
0
0,且
⨁
\bigoplus
⨁是按位异或的意思。
2.3 输出描述
输出文件为patrick.out。
共 M M M行,每行一个整数,代表每次询问的答案。
2.4 输入样例&输出样例
patrick.in
5 7 1
3 2 1 3 5 2
Q 1 1
Q 2 3
Q 3
C 2 2
C 5 3
C 4 2
Q 2
patrick.out
1
2
1
3
2.5 样例说明
对于第一个样例:
询问在解码后分别为 1 , 3 , 1 , 3 1,3,1,3 1,3,1,3。最后一次询问前的数列为 3 , 2 , 3 , 2 , 3 3,2,3,2,3 3,2,3,2,3
2.6 数据范围
保证通过异或之后, 1 ≤ M ≤ 5 × 1 0 5 , 1 ≤ A ≤ N ≤ 5 × 1 0 5 , 1 ≤ H i , C ≤ 5 × 1 0 5 , 1 ≤ B ≤ 5 × 1 0 5 . 1\leq M\leq 5\times 10^5,1\leq A\leq N\leq 5\times 10^5,1\leq H_i,C\leq 5\times 10^5,1\leq B \leq 5 \times 10^5. 1≤M≤5×105,1≤A≤N≤5×105,1≤Hi,C≤5×105,1≤B≤5×105.
题解:
算法一:
我们不维护连续段的个数,而是维护有多少个位置,数列突然从“小于某个数”变成了“大于等于某个数”。这个数量等于大于等于某个数的极长连续段的个数。如果
a
[
i
]
<
a
[
i
+
1
]
(
∀
i
∈
[
0
,
n
−
1
]
)
a[i] < a[i+ 1](\forall i \in [0,n-1])
a[i]<a[i+1](∀i∈[0,n−1]),那么在一个数据结构内,我们将
a
[
i
]
+
1
a[i] + 1
a[i]+1到
a
[
i
+
1
]
a[i+ 1]
a[i+1]区间加1。询问时在这个数据结构内单点询问即可。
复杂度
O
(
(
n
+
m
)
l
o
g
H
)
O((n+m)logH)
O((n+m)logH),期望得分
100
100
100。
算法二:
考虑“大于等于
x
x
x”连续段的个数,等于“该位置大于等于
x
x
x”的数目,减去“相邻俩位置都大于等于
x
x
x”的数目。即对于询问
x
x
x,我们需要知道
∑
i
=
1
n
[
a
i
≥
x
]
和
∑
i
=
1
n
−
1
[
m
i
n
{
a
i
,
a
i
+
1
}
≥
x
]
\sum_{i=1}^n[a_i\geq x]和\sum_{i=1}^{n-1}[min\lbrace a_i,a_{i+1}\rbrace \geq x]
∑i=1n[ai≥x]和∑i=1n−1[min{ai,ai+1}≥x],这两者可以利用数据结构快速维护。
复杂度
O
(
(
n
+
m
)
l
o
g
H
)
O((n+m)logH)
O((n+m)logH),期望得分100。
思路:
两个树状数组
(
t
r
e
e
s
u
m
,
t
r
e
e
m
i
n
)
(treesum,treemin)
(treesum,treemin),分别维护
∑
i
=
1
n
[
a
i
≥
x
]
和
∑
i
=
1
n
−
1
[
m
i
n
{
a
i
,
a
i
+
1
}
≥
x
]
\sum_{i=1}^n[a_i\geq x]和\sum_{i=1}^{n-1}[min\lbrace a_i,a_{i+1}\rbrace \geq x]
∑i=1n[ai≥x]和∑i=1n−1[min{ai,ai+1}≥x],即:以水面高度做下标,节点高度小于水面高度的节点数量,和,以
m
i
n
{
a
i
,
a
i
+
1
}
min\lbrace a_i,a_{i+1} \rbrace
min{ai,ai+1}做下标,相邻两节点较小值小于水面高度的节点数量(类似逆序对)。
当水面高度为
h
i
g
h
high
high时,有节点高度大于等于水面高度的节点数量减去相邻两节点较小值大于等于水面高度的节点数量,即为岛屿数量。
即
(
t
r
e
e
s
u
m
m
a
x
−
t
r
e
e
s
u
m
h
i
g
h
)
−
(
t
r
e
e
m
i
n
m
a
x
−
t
r
e
e
m
i
n
h
i
g
h
)
(treesum_{max}-treesum_{high})-(treemin_{max}-treemin_{high})
(treesummax−treesumhigh)−(treeminmax−treeminhigh)
参考下图:对于这个岛屿,
t
r
e
e
s
u
m
treesum
treesum为
k
k
k(共
k
k
k个节点大于等于
h
i
g
h
high
high),
t
r
e
e
m
i
n
treemin
treemin为
k
−
1
k-1
k−1(以
(
n
−
k
)
到
(
n
−
1
)
(n-k)到(n-1)
(n−k)到(n−1)为
a
a
a的每对
m
i
n
min
min都大于等于
h
i
g
h
high
high,
m
i
n
(
a
n
,
a
n
+
1
)
小
于
等
于
h
i
g
h
min(a_n,a_{n+1})小于等于high
min(an,an+1)小于等于high)。那么对于每一座岛屿,都有
(
t
r
e
e
s
u
m
m
a
x
−
t
r
e
e
s
u
m
h
i
g
h
)
−
(
t
r
e
e
m
i
n
m
a
x
−
t
r
e
e
m
i
n
h
i
g
h
)
=
=
1
(treesum_{max}-treesum_{high})-(treemin_{max}-treemin_{high})==1
(treesummax−treesumhigh)−(treeminmax−treeminhigh)==1。
注意:当
f
r
e
a
d
和
f
w
r
i
t
e
fread和fwrite
fread和fwrite卡常卡不过时,换成
c
i
n
和
c
o
u
t
(
要
c
o
u
t
<
<
cin和cout(要cout<<
cin和cout(要cout<<
′
'
′\n
′
'
′)有奇效。
代码:
#include<bits/stdc++.h>
using namespace std;
#define re register
#define in Read()
inline int in{
int s=0;char x;
for(x=getchar();!isdigit(x);x=getchar());
for( ;isdigit(x);x=getchar()) s=(s<<1)+(s<<3)+(x&15);
return s;
}
const int A=5e6+5;
int n,m;
char x[10];
int a,b,last;
int h[A];
struct Tree{
int tree[A];
inline int lowbit(int x){return x&(-x);}
inline void add(int w,int v){while(w<=A){tree[w]+=v;w+=lowbit(w);}}
inline int find(int w){int num=0;while(w>0){num+=tree[w];w-=lowbit(w);}return num;}
}t_num,t_min;
signed main(){
//freopen("patrick.in","r",stdin);
//freopen("patrick.out","w",stdout);
n=in,m=in;
for(re int i=1;i<=n;i++)
h[i]=in;
for(re int i=1;i<=n;i++){
t_num.add(h[i],1);
if(i!=n) t_min.add(min(h[i],h[i+1]),1);
}
while(m--){
scanf("%s",x);
if(x[0]=='Q'){
a=in;a^=last;
int xx=t_num.find(A)-t_num.find(a-1),yy=t_min.find(A)-t_min.find(a-1);
last=xx-yy;cout<<last<<'\n';
}
if(x[0]=='C'){
a=in,b=in;a^=last,b^=last;
t_num.add(h[a],-1);
if(a!=1&&a!=n) t_min.add(min(h[a-1],h[a]),-1),t_min.add(min(h[a],h[a+1]),-1);
if(a==1) t_min.add(min(h[a],h[a+1]),-1);
if(a==n) t_min.add(min(h[a-1],h[a]),-1);
h[a]=b;
t_num.add(h[a],1);
if(a!=1&&a!=n) t_min.add(min(h[a-1],h[a]),1),t_min.add(min(h[a],h[a+1]),1);
if(a==1) t_min.add(min(h[a],h[a+1]),1);
if(a==n) t_min.add(min(h[a-1],h[a]),1);
}
}
return 0;
}