一个显然的结论是,从高位道低位考虑答案在这一位是否可以是1,那么如果一个高位可以为1,那么一定不会为了其他低位而把它变成0。另一个结论是:如果一个高位不能变成1,那么这个高位不会对后续答案的考查产生任何限制。
接下来思考对于一个位,什么时候答案在这位可以变成1。注意到对于第1高位,统计A数组中这一位0的个数,考虑B数组中这一位1的个数,如果两个数相同,那么第一高位可以是1。
随后,我们考察它对于后续的位会产生什么限制。当我们确定第1高位一定是1以后,所有的第一高位是0的Ai,一定只会和第一高位是1的Bj组合在一起;同时只要满足前一条,这种组合就是任意的。除此之外,我们知道上述限制中,Ai和Bj的总个数相同。所以一个策略是:我们考虑按照第1高位把原数列分段,接下来每一位的匹配都发生在某一段内部。具体地,对于A在统计有多少最高位是0的过程中,把所有最高位是0的Ai交换到当前段的开头(对于第1高位,这就是整个A的开头),对于B也是如此。
注意,这一步只不过进行预处理,还没有把当前段分裂开。这是因为,对于当前考查的位,必须把每一段都考察完,我们才能确定每一段是否都满足“A中0等于B中1”的性质,也即答案的当前位可否为1。
接下来,我们进行如下处理。如果上述结论是可以为1,那么我们对于刚才考察过程中记录下来的每一段的“分裂点”,也就是在这一段内部,满足“前面都是A为0、B为1,后面都不是”的这个点,把它同步到所有分裂点的集合中。而如果结论是不是,我们就直接什么也不做,虽然我们在考察过程中,已经把AB数组进行了分类,但是这种分类只是段内的重新排序,一个显然结论是,这种重新排序不会影响答案(就像A或B输入时的顺序不影响排序一样,有意义的只是集合而不是顺序)。所以只要不同步分裂点就等于啥也没做。
我的代码以下供参考。其中lir标记一个点是否为分裂点,nir标记一个点是否是当前这位考察过程中处理出的潜在的分裂点。
#include <bits/stdc++.h>
using std::vector;
using std::max; using std::min;
typedef long long LL;
typedef unsigned long long UL;
int a[100005], b[100005];
int lir[100005], nir[100005];
int main(void)
{
int tsk; scanf("%d", &tsk);
while (tsk--)
{
int n; scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; ++i)
{
scanf("%d", &b[i]);
}
for (int i = 1; i <= n; ++i)
{
lir[i] = 0;
}
int ans = 0;
for (int d = 29; d >= 0; --d)
{
for (int i = 1; i <= n; ++i)
{
nir[i] = 0;
}
int t1 = 1, t2 = 1;
int ok = 1;
for (int i = 1; i <= n; ++i)
{
if (lir[i])
{
if (t1 == t2)
{
nir[t1] = 1;
}
else
{
ok = 0;
break;
}
t1 = t2 = i;
}
if ((a[i] >> d) & 1)
{
std::swap(a[i], a[t1++]);
}
if (!((b[i] >> d) & 1))
{
std::swap(b[i], b[t2++]);
}
}
if (t1 == t2)
{
nir[t1] = 1;
}
else
{
ok = 0;
}
if (ok)
{
ans += (1 << d);
for (int i = 1; i <= n; ++i)
{
if (nir[i]) lir[i] = 1;
}
}
}
printf("%d\n", ans);
}
return 0;
}
标签:std,CF1721D,高位,int,long,100005,分裂,Edu134Div2
From: https://www.cnblogs.com/kyeecccccc/p/16632308.html