洛谷 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 \not = 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\n",s+1);
	return 0;
}

posted @ 2020-03-16 15:05  bifanwen  阅读(224)  评论(0编辑  收藏  举报