题目传送
题意
给定一个正整数数组的前缀异或和和后缀异或和数组,在这两个异或和数组里共有原数组长度个数是未知数,求出任意一组可能的原数组。
思路
令前缀异或和数组的每个数为 pi,代表的是 a1∼ai 所有数的异或和(即 pi=a1⊕a2⊕⋯⊕ai)。后缀异或和数组的每个数为 sj, 代表的是 aj∼an 所有数的异或和(即 sj=aj⊕aj+1⊕⋯⊕an)。那么如果让 j=i+1(i≥1,j≤n),pi⊕sj 的结果不就是 a1∼an 所有数的异或和了吗?有人可能问:如果 −1 把这些组合的一边遮住了,怎么办?例如:
1 2
| p |3 34 367 -1 s |3178 -1 -1 -1
|
我们可以将两个异或和数组的首尾视作有 0,这样,就相当于增多了两个组合,分别是:p0,s1 和 pn,sn+1(其实就是 0 和 a1⊕a2⊕⋯⊕an)。
1 2
| p |0 3 34 367 -1 0 s |0 3178 -1 -1 -1 0
|
接着可以从题目中发现一个重要的关键点,那就是:
现在 Tommy 一共将 p 和 s 的 n 个元素换成 −1。
这个有什么用呢?增加了两个组合后 i,j 的取值范围就变成了 i∈[0,n],j∈[1,n+1],组合数正好比原数组长度多 1。也就是说,无论如何覆盖,都可以求出 a1∼an 所有数的异或和。知道这个后,就可以将一些组合的未知数给求出来。但是,如果一组中两个数都是未知,那怎么办呢?
假设!我们就可以把推不出的组合 (pi,si+1) 的 pi 全部假设成一个 <260 的数。显然可以发现假设的 pi 一定能使得 ai=pi⊕pi−1 合法。这样子就把 ai 的一个解算出来了。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include<bits/stdc++.h> using namespace std; int n; long long p[100005],s[100005]; long long total; int main(){ ios::sync_with_stdio(false); cin.tie();cout.tie(); int t; cin>>t; while(t--){ cin>>n; for(int i=1;i<=n;++i) cin>>p[i]; for(int i=1;i<=n;++i) cin>>s[i]; for(int i=0;i<=n;++i){ if(p[i]!=-1&&s[i+1]!=-1) { total=p[i]^s[i+1]; break; } } for(int i=0;i<=n;++i){ if(p[i]==-1&&s[i+1]!=-1) { p[i]=s[i+1]^total; } else if(p[i]!=-1&&s[i+1]==-1) { s[i+1]=p[i]^total; } } for(int i=1;i<=n;++i){ if(p[i]==-1) p[i]=1; } for(int i=1;i<=n;++i){ cout<<(p[i]^p[i-1])<<" "; } cout<<"\n"; } return 0; }
|
2022/7/15 修改
2025/10/10 第二次修改