洛谷 P5639 【CSGRound2】守序者的尊严 题解

原题链接

简要题意:

(1) 号位开始走,可以连续走过一段连续的 (0) ,每走一次,所有位置取反。 (即 (0 gets 1)(1 gets 0)).

算法一

模拟暴力即可。

每次从当前位置模拟往后走到能走的最后一个点(即当前连续 (0) 的最后一个 (0) 的位置),然后暴力将每个位置取反。

时间复杂度: (O(n^2)).

实际得分:(50pts).

算法二

基于暴力,你可能想到这是线段树?

可是,这维护的是什么?不可而知。

每位取反?无法维护。

连续一段?倒是可以,但是取反维护不了啊……

所以,算法二 线段树 咕咕咕了。

算法三

你会发现,给出的数组必然是这样的一段:

(0 0 0 0 0 0 ... 1 1 1 ... 0 0 0 ... 1 1 ...)

也就是,一段连续的 (0) 和 一段连续的 (1) 交错而成。

我们需要跳开修改,解决问题。

以样例为例:

(0 0 1 1 0 1)

我们第一次走到 (2) 的位置,然后变成:

(1 1 0 0 1 0)

你会发现,如果不修改的话,其实你只要再走一段连续的 (1) ,和修改后走一段连续的 (0) 是一样的。

走到 (4) 的位置,然后变成:

(0 0 1 1 0 1)

你会发现,如果不修改的话,其实你只要再走一段连续的 (0) ,和修改后走一段连续的 (1) 是一样的。

所以,第偶数步我们走连续的 (0) ,奇数步走连续的 (1) ,模拟一遍。

时间复杂度: (O(n)).

实际得分: (100pts).

算法四

显然我们考虑另一个常数性的优化。

你会发现,你并不在乎 连续的一段有多少个 ,而关心 一共有多少段 ,这就是我们的答案。

那么,总段数其实就是 (a_i ot = a_{i-1}) 的个数再 (+1) .

很好理解:一开始只有一段,每改变一次就产生新的一段。

显然,这个无论从码量,时间都比算法三要优一些。

时间复杂度: (O(n)).

实际得分: (100pts).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=1e6+1;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n,a[N];
int s=0;

int main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=2;i<=n;i++) s+=(a[i]!=a[i-1]);
	printf("%d
",s+1);
	return 0;
}

原文地址:https://www.cnblogs.com/bifanwen/p/12503990.html