(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行,每行有如下两种可能的格式:
题解:
算法一:
我们不维护连续段的个数,而是维护有多少个位置,数列突然从“小于某个数”变成了“大于等于某个数”。这个数量等于大于等于某个数的极长连续段的个数。如果 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
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;
}