这个题的前缀和可能没有那么直观,上述博文中认为这种思路是差分,我觉得类比扫描线理解可能更直观一些:首先需要比较直观的观察,建议考场画图看,也就是说只有
A
[
i
−
1
]
<
A
[
i
]
A[i-1]<A[i]
A[i−1]<A[i] 的情况才有可能产生一个非零段,产生的条件是
p
p
p 落在
A
[
i
−
1
]
A[i-1]
A[i−1] 和
A
[
i
]
A[i]
A[i] 中间。思路的巧妙之处是不需要考虑下降段,也就是说使用上升段代替了整个非零段进行计数;对于每一组
A
[
i
−
1
]
<
A
[
i
]
A[i-1]<A[i]
A[i−1]<A[i] ,可以使用一个大数组
d
i
f
f
diff
diff 记录两个端点,即
d
i
f
f
[
A
[
i
−
1
]
+
1
]
+
+
,
d
i
f
f
[
A
[
i
]
+
1
]
−
−
diff[A[i-1]+1]++, diff[A[i]+1]--
diff[A[i−1]+1]++,diff[A[i]+1]−−。也就是说,当
p
p
p 到达
d
i
f
f
[
A
[
i
−
1
]
+
1
]
diff[A[i-1]+1]
diff[A[i−1]+1] 的时候,非零段数目增加
1
1
1,到达
d
i
f
f
[
A
[
i
]
+
1
]
diff[A[i]+1]
diff[A[i]+1] 的时候,非零段的数目减少
1
1
1。最终
p
p
p 时的非零段数目即是
∑
i
=
1
p
d
i
f
f
[
i
]
\sum_{i=1}^p diff[i]
∑i=1pdiff[i] 的结果。
需要注意的细节是,运行算法之前需要对原数组
A
A
A 收尾各添一个
0
0
0 作为起始点。
代码实现
实现一:70分,以非零段为单位的处理
#include<iostream>#include<vector>#include<algorithm>#defineSEG_OUT0#defineSEG_IN1structNone_zero_seg{None_zero_seg(None_zero_seg * nxt, None_zero_seg * pre,int l,int r,int lst,int lz):next(nxt),prev(pre),left(l),right(r),least(lst),lazy(lz){}
None_zero_seg * next;
None_zero_seg * prev;int left, right;int least;int lazy;};structNone_zero_segs{None_zero_segs():head(nullptr),tail(nullptr),total(0){}
None_zero_seg * head;
None_zero_seg * tail;int total;};usingnamespace std;intmain(){int n;scanf("%d",&n);int A[n+2];
A[0]= A[n+1]=0;
None_zero_segs Nzs;
Nzs.head =newNone_zero_seg(nullptr,nullptr,0,0,0,0);
Nzs.tail = Nzs.head;bool state = SEG_OUT;int left, seg_min, all_max =0;for(int i =1; i <= n; i++)scanf("%d",&A[i]);for(int i =1; i <= n+1; i++){
all_max =max(all_max, A[i]);if(state == SEG_OUT && A[i]!=0){
state = SEG_IN;
left = i;
seg_min = A[i];}elseif(A[i]!=0) seg_min =min(seg_min, A[i]);elseif(state == SEG_IN && A[i]==0){
state = SEG_OUT;auto*new_seg =newNone_zero_seg(nullptr, Nzs.tail, left, i-1, seg_min,0);// Mark
Nzs.total++;
Nzs.tail->next = new_seg;
Nzs.tail = new_seg;}else;}int total_max = Nzs.total;for(int p =1; p <= all_max; p++){for(None_zero_seg * seg = Nzs.head->next; seg !=nullptr; seg = seg->next){// 注意如果拆了之后要seg = 拆了之后的最后一个元素if(seg->least >= p)continue;
Nzs.total--;
None_zero_seg * tmp_tail = seg->prev;
state = SEG_OUT;for(int i = seg->left; i <= seg->right; i++){if(state == SEG_OUT && A[i]>= p){
state = SEG_IN;
left = i;
seg_min = A[i];}if(state == SEG_IN &&(A[i]< p || i == seg->right)){// 注意不是else if,不然边界条件无法收尾
state = SEG_OUT;auto*new_seg =newNone_zero_seg(nullptr, tmp_tail, left, i == seg->right ? i : i-1, seg_min,0);// Mark
Nzs.total++;
tmp_tail->next = new_seg;
tmp_tail = new_seg;}if(A[i]>= p) seg_min =min(seg_min, A[i]);if(A[i]< p) A[i]=0;}
tmp_tail->next = seg->next;if(seg->next !=nullptr) seg->next->prev = tmp_tail;delete seg;
seg = tmp_tail;}
total_max =max(total_max, Nzs.total);}printf("%d", total_max);return0;}
实现二:100分,基于前缀和的巧妙处理
#include<iostream>#defineMAX_n1000000#defineMAX_N10000int diff[MAX_N];int A[MAX_n];usingnamespace std;intmain(){int n;scanf("%d",&n);for(int i =1; i <= n; i++)scanf("%d",&A[i]);for(int i =0; i <= n+1; i++){if(A[i]> A[i-1]){
diff[A[i-1]+1]++;
diff[A[i]+1]--;}}int total_max =0, prefix =0;for(int p =1; p < MAX_N; p++){
prefix += diff[p];
total_max =max(prefix, total_max);}printf("%d", total_max);}