新年的XOR
题目描述
在动物行为学中,Alpha对应动物群体中等级最高的个体,简而言之,就是人赢,那么AlphaGo自然就是人赢狗了。
但是狗群中还有众多的SingleDog,平日里他们和AlphaGo和谐相处,忍受着酸臭味自得其乐的活下去。但在2月14日这天,当AlphaGo在SingleDog们最后的防线——朋友圈秀恩爱的时候,他们实在按耐不住,决定密谋用FFF团的黑暗力量,打败AlphaGo。
但是AlphaGo的智商很高,很快便拦截了SingleDog们传输消息的异或值。为了让AlphaGo相信,SingleDog们只是在数朋友圈里的狗粮数,你,勇敢无畏的跳蚤,决定编造一个弥天大谎。
AlphaGo截获的是一个整数 n n n,你需要构造一个长度大于 1 1 1 的区间 [ L , R ] [L,R] [L,R]使得区间中所有整数的异或和恰好为 n n n。
输入格式
第一行输入一个整数 t t t 表示数据组数。
接下来 t t t 行每行一个整数 n n n。
输出格式
每组数据输出两个空格隔开的整数 L , R L,R L,R,表示你构造的区间。要求 1 ≤ L < R ≤ 1 0 1 8 1≤L<R≤10^18 1≤L<R≤1018。
输入保证存在这样的区间。
样例
input
3
0
4
12
output
8 67
97 100
87 90
限制与约定
测试点编号 | n n n的规模 | 其他 |
---|---|---|
1,2 | n ≤ 100 n≤100 n≤100 | 无 |
3,4 | n ≤ 2 × 1 0 3 n≤2×10^3 n≤2×103 | 无 |
5,6 | n ≤ 1 0 6 n≤10^6 n≤106 | 无 |
7,8 | n ≤ 1 0 18 n≤10^{18} n≤1018 | 存在 ∥ R − n ∥ ≤ 100 \|R−n\|≤100 ∥R−n∥≤100的合法解 |
9,10 | n ≤ 1 0 18 n\leq10^{18} n≤1018 | 无 |
对于
100
100%
100 的数据,
n
≥
0
,
1
≤
t
≤
100
n≥0,1≤t≤100
n≥0,1≤t≤100、
时间限制:
1
s
1s
1s
空间限制:
256
M
B
256MB
256MB
分析
一点一点来。
我们可以枚举区间,然后暴力求异或和。
Θ
(
n
3
)
\Theta(n^3)
Θ(n3)。
我们可以求前缀异或和,于是枚举对于每个区间就可以
Θ
(
1
)
\Theta(1)
Θ(1)计算异或和了,
Θ
(
n
2
)
\Theta(n^2)
Θ(n2)。
于是我们想到用个桶之类的东西,一边求前缀异或和,一边存对于每个异或前缀和值在哪个位置出现过了,如果
S
[
i
]
⊕
n
S[i]\oplus n
S[i]⊕n已经出现了,就找到了。
因为
a
⊕
b
=
c
⟺
a
⊕
c
=
b
a\oplus b=c \iff a\oplus c=b
a⊕b=c⟺a⊕c=b,而我们求前缀异或和(记
S
[
x
]
S[x]
S[x]为1~x的前缀异或和)
n
=
S
[
r
]
⊕
S
[
l
−
1
]
n=S[r]\oplus S[l-1]
n=S[r]⊕S[l−1]就
⟺
S
[
l
−
1
]
=
S
[
r
]
⊕
n
\iff S[l-1]=S[r]\oplus n
⟺S[l−1]=S[r]⊕n了。
于是现在优化到了
Θ
(
n
)
\Theta(n)
Θ(n)。
打了个表发现惊奇的规律,
{
n
%
4
=
0
时
,
S
[
n
]
=
n
。
n
%
4
=
1
时
,
S
[
n
]
=
1
。
n
%
4
=
2
时
,
S
[
n
]
=
n
+
1
。
n
%
4
=
3
时
,
S
[
n
]
=
0
。
\begin{cases}n\%4=0时,S[n]=n。\\ n\%4=1时,S[n]=1。\\ n\%4=2时,S[n]=n+1。\\n\%4=3时,S[n]=0。\end{cases}
⎩⎪⎪⎪⎨⎪⎪⎪⎧n%4=0时,S[n]=n。n%4=1时,S[n]=1。n%4=2时,S[n]=n+1。n%4=3时,S[n]=0。
这也非常容易证明,因为
(
2
k
)
⊕
(
2
k
+
1
)
=
1
(2k)\oplus(2k+1)=1
(2k)⊕(2k+1)=1,如果n是奇数,后面的若干对奇偶双双异或起来,都抵消成了1,总共
n
−
1
2
\frac{n-1}{2}
2n−1个,然后把这些个1和最前头的孤单的1异或起来,就得到了
S
[
n
]
=
n
+
1
2
个
1
的
异
或
和
=
n
+
1
2
&
1
S[n]=\frac{n+1}{2}个1的异或和=\frac{n+1}{2}\&1
S[n]=2n+1个1的异或和=2n+1&1;如果n是偶数,你既可以把后面的若干对奇偶双双异或起来再异或起1和2,也可以
S
[
n
]
=
S
[
n
−
1
]
⊕
n
S[n]=S[n-1]\oplus n
S[n]=S[n−1]⊕n,所以
S
[
n
]
=
n
+
(
n
+
1
2
&
1
)
S[n]=n+(\frac{n+1}{2}\&1)
S[n]=n+(2n+1&1)。
(去年的NOIp2030模拟赛用了这个结论但没有证明,看来我比去年强了一些。)
(因为去年是N合1,这个东西不是重点)
P.S.:
(
2
k
)
⊕
(
2
k
−
1
)
≠
1
(2k)\oplus(2k-1)\neq 1
(2k)⊕(2k−1)=1
S
[
5
]
=
1
⊕
2
⊕
3
⊕
4
⊕
5
=
1
⊕
(
2
⊕
3
)
⊕
(
4
⊕
5
)
=
1
⊕
1
⊕
1
=
1
S[5]=1\oplus 2\oplus 3\oplus 4\oplus 5=1\oplus (2\oplus 3)\oplus (4\oplus 5)=1\oplus1\oplus1=1
S[5]=1⊕2⊕3⊕4⊕5=1⊕(2⊕3)⊕(4⊕5)=1⊕1⊕1=1
S
[
6
]
=
S
[
5
]
⊕
6
=
1
⊕
2
⊕
3
⊕
4
⊕
5
⊕
6
=
1
⊕
(
2
⊕
3
)
⊕
(
4
⊕
5
)
⊕
6
=
1
⊕
6
=
7
S[6]=S[5]\oplus 6=1\oplus 2\oplus 3\oplus 4\oplus 5 \oplus 6=1\oplus (2\oplus 3)\oplus (4\oplus 5)\oplus 6=1\oplus 6=7
S[6]=S[5]⊕6=1⊕2⊕3⊕4⊕5⊕6=1⊕(2⊕3)⊕(4⊕5)⊕6=1⊕6=7
这个结论有什么用呢?
如果
n
%
4
=
0
n\%4=0
n%4=0,那就输出[1,n];
如果
n
%
4
=
2
n\%4=2
n%4=2,那就输出[2,n];
如果
n
%
4
=
3
n\%4=3
n%4=3,那就输出[1,n-1];
如果
n
%
4
=
1
n\%4=1
n%4=1,因为
S
[
n
−
1
]
=
n
−
1
=
S
[
n
−
4
]
⊕
(
n
−
3
)
⊕
(
n
−
2
)
⊕
(
n
−
1
)
=
1
⊕
(
n
−
3
)
⊕
(
n
−
2
)
⊕
(
n
−
1
)
S[n-1]=n-1=S[n-4]\oplus(n-3)\oplus(n-2)\oplus(n-1)=1\oplus(n-3)\oplus(n-2)\oplus(n-1)
S[n−1]=n−1=S[n−4]⊕(n−3)⊕(n−2)⊕(n−1)=1⊕(n−3)⊕(n−2)⊕(n−1),于是发现
(
n
−
3
)
⊕
(
n
−
2
)
⊕
(
n
−
1
)
=
n
(n-3)\oplus(n-2)\oplus(n-1)=n
(n−3)⊕(n−2)⊕(n−1)=n,所以输出[n-3,n-1]。
(
(
n
−
3
)
⊕
(
n
−
2
)
⊕
(
n
−
1
)
=
偶
⊕
奇
⊕
偶
=
奇
,
奇
⊕
1
=
奇
−
1
(n-3)\oplus(n-2)\oplus(n-1)=偶\oplus 奇\oplus偶=奇,奇\oplus 1=奇-1
(n−3)⊕(n−2)⊕(n−1)=偶⊕奇⊕偶=奇,奇⊕1=奇−1)。
然后对于0、1、2特判一下。
于是A了。
翻了翻排名前几(十)名的代码,发现他们都是对于n是奇数输出
[
n
−
3
,
n
−
1
]
[n-3,n-1]
[n−3,n−1],对于n是偶数输出
[
n
−
4
,
n
]
[n-4,n]
[n−4,n],然后特判0~4。
也是对的,挺好证明,可以按照n%4的余数分类,每一类就用上面的结论就好了。
代码
#include<cstdio>
#include<iostream>
using namespace std;
long long T,n;
int main()
{
cin>>T;
while(T--)
{
cin>>n;
if(n==1) puts("2 3");
else if(n==0) puts("1 3");
else if(n==2) puts("3 5");
else if(n%4==0) cout<<1<<" "<<n<<endl;
else if(n%4==2) cout<<2<<" "<<n<<endl;
else if(n%4==3) cout<<1<<" "<<n-1<<endl;
else cout<<n-3<<" "<<n-1<<endl;
}
}