CF1604B 题解
CF1604B 题解
题面
题意
给定一个长度为 \(n\) 的数组 \(a\),将 \(a\) 分成若干段,问是否有一种分段的方法使得每一段的最大上升子序列的长度的异或和为 \(0\)。
有多组数据。
前置知识
- 位运算,知道异或算法相同为假,不同为真,理解并能够运用 \(1\oplus 1=0\)。
- 数学归纳法(证明思路的时候会用到)。
思路(做法)
- 当 \(n\) 为偶数时,显然只需将 \(a\) 分成 \(n\) 段,每一段一个数字即可。这样每一段的最大上升子序列的长度为 \(1\),且有偶数个段,因为 \(1\oplus 1=0\),所以最终得到的结果也必为 \(0\)。
- 当 \(n\) 为奇数时,如果还是像上面这种简单粗暴的方法来构造的话,结果必定为 \(1\)(因为 \(n\) 为奇数)。所以就想到,要是有一个 \(i(i\in[1,n-1])\),使得 \(a_i\leqslant a_{i+1}\),的话,则把 \(a_i,a_{i+1}\) 放在一组,其余的一个数一组,这样的话,\(a_i,a_{i+1}\) 这一组的最大上升子序列的长度为 \(1\),总共有偶数个 \(1\) 异或,则结果为 \(1\)。若不存在这样的 \(i\),则无解。
思路正确性的证明
其实,上面的输出 YES 的方法不多说,不懂的话再看看题目,现在要证明为什么不存在 \(i(i\in[1,n-1])\) 使得 \(a_i\leqslant a_{i+1}\) 的话就无解。
首先,我们知道,要是不存在这样的 \(i\),则 \(a\) 为一个上升序列,所以不管怎么分段,分的那一段最大上升子序列的长度必定就为那一段的长度,那么就有以下证明:
引理:若有正整数 \(a,b\),则 \(a\oplus b\) 的结果的奇偶性与 \(a+b\) 的结果的奇偶性相同。
引理证明:
设 \(a,b\) 在二进制表达下的最后一位分别为 \(a',b'(a',b'\in\{0,1\})\),则有以下两种情况:
- \(a'=b'\),则由异或的运算规则,\(a\oplus b\) 的结果为偶数,又由加法的运算规则,\(a+b\) 的结果也为偶数。所以两者奇偶性相同。
- \(a'\neq b'\),则由异或的运算规则,\(a\oplus b\) 的结果为奇数,又由加法的运算规则,\(a+b\) 的结果也为奇数。所以两者奇偶性相同。
综上,\(a\oplus b\) 的结果的奇偶性与 \(a+b\) 的结果的奇偶性相同。
证毕。
回到证明思路的正确性,咱们设每一段的最大上升子序列的长度为 \(b\) 数组,一共分成了 \(m\) 段。则 \(b_1+b_2+b_3+\cdots+b_m=n\) 且为奇数。
由引理,\(b_1\oplus b_2\) 奇偶性与 \(b_1+b_2\) 相同。
同理,\((b_1\oplus b_2)\oplus b_3\) 奇偶性与 \((b_1+b_2)+b_3\) 相同。(把 \(b_1+b_2\) 看成一个整体。)
由数学归纳法得,\(b_1\oplus b_2\oplus b_3\oplus \cdots\oplus b_n\) 奇偶性与 \(b_1+b_2+b_3+\cdots+b_m\) 相同。
所以,\(b_1\oplus b_2\oplus b_3\oplus \cdots\oplus b_n\) 为奇数。
而 \(0\) 为偶数,所以这种情况必定输出 NO。
证毕。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
long long T,n,a[100005];
bool flag;
int main(){
scanf("%lld",&T);//多组数据
while(T--){
flag=false;//初始化
scanf("%lld",&n);
if(n%2==0) flag=true;//直接可以
for(int i=0; i<n; i++){
scanf("%lld",&a[i]);
if(i&&a[i]<=a[i-1]) flag=true;//这也是可以的
}
if(flag) printf("YES\n");
else printf("NO\n");
}
return 0;
}

浙公网安备 33010602011771号