前言
美团笔试是5道编程题,只做出来3.5道(当时赶时间上学校,只做了一个小时二十分钟就交了,也可能是题目有点难,毕竟字节笔试只做了半个小时就AC了。。。),前两题是水题,就不说了。这里说一下比较有意思的第三题------可能是运气好,在纸上画了十多分钟就出思路了。
题意
令 b i = a i ⊕ 1 % i ⊕ 2 % i ⊕ . . . ⊕ n % i b_{i}=a_{i} \oplus 1\%i \oplus 2\%i \oplus ... \oplus n\%i bi=ai⊕1%i⊕2%i⊕...⊕n%i,要求 b 1 ⊕ b 2 ⊕ . . . ⊕ b n b_{1} \oplus b_{2} \oplus ... \oplus b_{n} b1⊕b2⊕...⊕bn,其中 n < 1 0 5 n<10^{5} n<105, a i a_{i} ai题中会给出。
思路
要求的结果无非是如下数字的异或。
a
1
⊕
a
2
⊕
.
.
.
⊕
a
n
a_{1}\oplus a_{2}\oplus ...\oplus a_{n}
a1⊕a2⊕...⊕an
1
%
1
⊕
2
%
1
⊕
.
.
.
⊕
n
%
1
1\%1\oplus 2\%1\oplus ...\oplus n\%1
1%1⊕2%1⊕...⊕n%1
1
%
2
⊕
2
%
2
⊕
.
.
.
⊕
n
%
2
1\%2\oplus 2\%2\oplus ...\oplus n\%2
1%2⊕2%2⊕...⊕n%2
.
.
.
...
...
1
%
n
⊕
2
%
n
⊕
.
.
.
⊕
n
%
n
1\%n\oplus 2\%n\oplus ...\oplus n\%n
1%n⊕2%n⊕...⊕n%n
化简后如下。
a
1
⊕
a
2
⊕
.
.
.
⊕
a
n
a_{1}\oplus a_{2}\oplus ...\oplus a_{n}
a1⊕a2⊕...⊕an
0
⊕
0
⊕
.
.
.
⊕
0
0\oplus 0\oplus ...\oplus 0
0⊕0⊕...⊕0
1
⊕
2
⊕
0
⊕
1
⊕
2
⊕
0
⊕
.
.
.
⊕
n
%
2
1\oplus 2\oplus 0\oplus 1\oplus 2\oplus 0\oplus ...\oplus n\%2
1⊕2⊕0⊕1⊕2⊕0⊕...⊕n%2
1
⊕
2
⊕
3
⊕
0
⊕
1
⊕
2
⊕
3
⊕
0
⊕
.
.
.
⊕
n
%
3
1\oplus 2\oplus 3\oplus 0\oplus 1\oplus 2\oplus 3\oplus 0\oplus ...\oplus n\%3
1⊕2⊕3⊕0⊕1⊕2⊕3⊕0⊕...⊕n%3
.
.
.
...
...
1
⊕
2
⊕
.
.
.
⊕
0
1\oplus 2\oplus ...\oplus 0
1⊕2⊕...⊕0
第一个式子直接累计异或即可,主要如何求下面的式子怎么求(对下面的式子从1开始编号)。
可以看出下面的式子是有循环节的,我们不如将其分组。
为了方便说明,假定
p
r
e
i
=
1
⊕
2
⊕
.
.
.
⊕
i
pre_{i}=1\oplus 2\oplus ... \oplus i
prei=1⊕2⊕...⊕i。
比如对于1个式子,每组一个数0,最终结果为
0
n
0^{n}
0n(注意这个幂代表n个0异或)。
对于第2个式子,每组2个数1和0,最终结果为
1
n
2
⊕
p
r
e
n
%
2
1^{\frac{n}{2}}\oplus pre_{n\%2}
12n⊕pren%2。
一般地,对于第i个式子,每组n个数1,2,…,i-1,0,最终结果为
p
r
e
i
−
1
n
i
⊕
p
r
e
n
%
i
pre_{i-1}^{\frac{n}{i}}\oplus pre_{n\%i}
prei−1in⊕pren%i。
p
r
e
i
−
1
n
i
pre_{i-1}^{\frac{n}{i}}
prei−1in可以用快速幂求(注意不是常规的快速幂,要将乘法转化为异或)。
时间复杂度分析
一共有n个式子,求每个式子需要快速幂,时间复杂度为 O ( log ( n ) ) \mathcal{O}(\log(n)) O(log(n)),总的时间复杂度为 O ( n log ( n ) ) \mathcal{O}(n\log(n)) O(nlog(n))。
实现
#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],pre[N];
int qpow(int a,int b){
int res=0,rem=a;
while(b){
if(b&1) res^=rem;
rem^=rem;
b>>=1;
}
return res;
}
int main(){
for(int i=1;i<N;i++){
pre[i]=i^pre[i-1];
}
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int res=0;
for(int i=1;i<=n;i++){
res^=a[i];
}
for(int i=1;i<=n;i++){
res^=qpow(pre[i-1],n/i)^pre[n%i];
}
cout<<res<<endl;
return 0;
}
补
之后发现可以不用快速幂,因为奇数个相同的数异或等于该数,偶数个相同的数异或等于0,所以只要判断 n i \frac{n}{i} in的奇偶就行了,时间复杂度为 O ( n ) \mathcal{O}(n) O(n)。不过那个数据量有意地让我联想到快速幂。