昨天花了好几个小时在瞎搞,还读错题,看错代码,终于是乱搞出来了,闲下来就写篇博客记录一下昨天差点想砸电脑的心态
观察别人神秘的代码,发现如下神秘的性质
对序列子集异或,超集异或,子集异或会使得序列翻转
本人太菜不知道怎么直观的推,只能靠打表看出结论后枚举贡献进行验证
例如
程序如下
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int P=10007;
int main()
{
int n;scanf("%d",&n);
vector<int> f(1<<n);
for(int i=0;i<1<<n;++i)f[i]=rand()%P;
printf("初序列 : ");
for(auto v:f)printf("%d ",v);
puts(" ");
for(int i=0;i<n;++i)for(int j=0;j<1<<n;++j)
if(j&1<<i)f[j]^=f[j^1<<i];
for(int i=0;i<n;++i)for(int j=0;j<1<<n;++j)
if(!(j&1<<i))f[j]^=f[j^1<<i];
for(int i=0;i<n;++i)for(int j=0;j<1<<n;++j)
if(j&1<<i)f[j]^=f[j^1<<i];
printf("终序列 : ");
for(int i=0;i<1<<n;++i)printf("%d ",f[i]);
}
题目中问题可以通过一系列转换,转换成已知b的(m-n,m]项,求a的[0,n)项,a的[n,m]全为0,并且对a进行子集异或将得到b
如果知道b的原序列,那么只需要一次超集异或+一次子集异或就能得到a,但很可惜b的前部分缺失,所以得去发掘其他性质
不难发现如果a的[n,m]全为0,那么对原序列b经行超集异或的时候就会使得b的p[0,m-n]全为0。
从结果开始思考,对原序列b进行超集异或的序列称为c,再对c进行子集异或的序列称为d。
其中d的前[0,m-n+1]项为0,而d是c子集异或的结果,并且c[0]=d[0]=0,所以有c[1]=d[1]=0,c[2]=d[2]=0。所以c的前[0,m-n+1]项也是0。
故对残缺序列b进行超集异或得到结果和原序列进行超集异或的结果相同,所以就解决了
当然,不放心的话也可以写个程序试一下,比如
代码如下
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int P=10007;
int main()
{
int n;scanf("%d",&n);
vector<int> f(1<<n);
for(int i=0;i<(1<<n)-10;++i)f[i]=rand()%P;
printf("初序列 : ");
for(auto v:f)printf("%d ",v);
puts(" ");
for(int i=0;i<n;++i)for(int j=0;j<1<<n;++j)
if(j&1<<i)f[j]^=f[j^1<<i];
for(int i=0;i<n;++i)for(int j=0;j<1<<n;++j)
if(!(j&1<<i))f[j]^=f[j^1<<i];
/* for(int i=0;i<n;++i)for(int j=0;j<1<<n;++j)
if(j&1<<i)f[j]^=f[j^1<<i];*/
printf("C序列 : ");
for(int i=0;i<1<<n;++i)printf("%d ",f[i]);
}
于是就做完了,对于该题有如下代码
当然也可以不用写的这么麻烦,可以写得更简洁许多
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
int main()
{
int n;scanf("%d",&n);
int pos=1,x=1;
while(n>x)x<<=1,++pos;
--x;
vector<int> f(1<<20);
for(int i=x;i>x-n;--i)
scanf("%d",&f[i]);
for(int i=0;i<pos;++i)
for(int j=x-n+1;j<=x;++j)
if(!(j&1<<i))f[j]^=f[j^1<<i];
for(int i=0;i<pos;++i)
for(int j=x-n+1;j<=x;++j)
if(j&1<<i)f[j]^=f[j^1<<i];
for(int i=x-n+1;i<=x;++i)printf("%d ",f[i]);
}