题解:CF2283C Big XOR
这是一篇特别详细的题解。
思路
考虑先把不变量求出来,而显然这道题的不变量只有
k ,于是可得:\begin{align*} k &= \bigvee_{i = 1}^n i \\ &= 2^{\log_2(n)+1}-1 \end{align*} 方便起见,令
h = \log_2(n) 。
由于
继续分析,令:
那么假设操作的下标集合为
由于
虽然该问题可以使用线性基的某种变形完成,但是这未免过于复杂了些,遂考虑简单做法。
这个时候,神之一手降临了!我们不妨大胆去做,直接删掉所有
这样做的正确性是什么呢?让我们分析一下。
先来考虑一下此时的
其中
i = 2^j 。
也就是说,令:
那么任意操作下标集合
也可以假定
A_{-1} = 0 。
假设我们知道了
因为
由于我们留下的
这就是只保留
2 的幂的好处。
接下来就是如何求出
于是就出现了一个两难的问题,两者皆可,选哪一个呢?
你可能会想到两者都跑一遍检测,哪个可以就选哪个。
这其实是可行的,不过判断条件是
遂考虑优雅做法。
我们令
:::info[当然这个结论是有严谨证明的] 首先证明:
诶等等,这似乎不需要证明了!
因为之前我们已经研究过:在
然后直接使用恒等式:
| 由于偶数加上偶数减去偶数还是偶数,所以证完了。 |
|---|
| 也就是说 |
而
也就是说题目一定有解!
莫名其妙地证明了题目已经给出的条件!?
其实也变相说明了我们只需要选择
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
bool a[N];
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int _;
cin >> _;
for(int __ = 1;__<=_;++__)
{
int n;
cin >> n;
int s = n&3,nn;
if(!s)
{
nn = n;
}
else if(s == 1)
{
nn = 1;
}
else if(s == 2)
{
nn = n+1;
}
else
{
nn = 0;
}
for(int i = 1;i<=n;++i)
{
a[i] = 0;
}
int h = 31-__builtin_clz(n),m = nn^((1<<h+2)-1),k = (1<<h+1)-1;
if(__builtin_popcount(m)&1)
{
m^=1;
}
bool cur = 0;
for(int i = 0;i<=h;++i)
{
cur^=(m>>i)&1;
if(cur)
{
a[1<<i] = 1;
}
}
for(int i = 1;i<=n;++i)
{
cout << a[i] << ' ';
}
cout << '\n';
}
return 0;
}
:::
推广
其实这个题是可以推广的:
在原题的基础上将数据范围改为
1 \le n \le 10^{18} ,然后把输出格式改成只输出要操作的下标。
很显然操作的下标数量是
而刚刚题目中的做法只有求
然后手算几个
不难发现:
当然还是要证明一下的。
首先有一个非常显然的引理:
这是因为这四个数在二进制下只有最低两位不同,而且分别是:
这四个东西异或起来就是
然后根据这个引理就很容易得到刚刚发现的规律了。
最后根据定义
不得不说这个方法是真的太神奇了。
然后就做完了。
总结
感觉操作类型的构造题就是先把不变量算了,先算出不操作时的数值,然后考虑操作的增量,进行一连串神秘的分析即可。
尾声
如果你也想让这篇题解被更多萌新看到,请你点一个小小的赞,助力我的文章造福更多萌新,谢谢你们!