【2021.2.27省选模拟】 集合
提答题。(其实也可以是传统题)
在\([1,n]\)的整数中选出一个集合,要求两两异或不同。让这个集合尽量大。
\(n< 2^{24}\)
答案阈值大概在\(2^k\)级别,其中\(2^{2k}=O(n)\)。
做法:定义一个多项式的域,多项式系数模\(2\),模某个\(k\)次不可约多项式意义下。对于\(i\in [1,2^k)\)(\(i\)为一个多项式,用二进制表示),把二元组\((i^3,i)\)作为答案(即将两个二进制数拼接在一起)。此时答案为\(2^k-1\),补一个\(2^{2k}\)(二进制数)就有\(2^k\)了。
注意可能要进行一些微调。
(下面不区分加法和异或,并且加法和减法等价)
证明:考虑在这个意义下,如果存在\((a^3,a)+(b^3,b)=(c^3,c)+(d^3,d)\),即\(a^3+b^3=c^3+d^3,a+b=c+d\),则:
\[a^2+ab+b^2=c^2+cd+d^2\\
ab=cd\\
在方程x^2-(a+b)x+ab=0中,\{a,b\}和\{c,d\}都是解。\\
\therefore \{a,b\}=\{c,d\}
\]
所以如果\(a,b,c,d\),则不可能冲突。
另外找\(k\)次不可约多项式可以直接类似于素数筛法搞。
using namespace std;
#include <bits/stdc++.h>
int n,k;
const int tab[13]={1,2,7,11,19,37,67,131,283,515,1033,2053,4105};
int mul(int a,int b){
int c=0;
for (int i=0;i<=k;++i)
if (a>>i&1)
c^=b<<i;
return c;
}
int lg(int a){
int s=0;
for (;a;a>>=1)
s++;
return s-1;
}
int di(int c,int a){
int _c=c,b=0,w=lg(a);
for (int i=k+k-1;i>=w;--i)
if (c>>i&1){
b^=1<<i-w;
c^=a<<i-w;
}
// if ((mul(a,b)^c)!=_c)
// printf("fuck\n");
return b;
}
int mod(int a,int p){
return a^mul(di(a,p),p);
}
int ans[1<<25|5];
int bz[1<<25|5];
int main(int argc,char *argv[]){
freopen(argv[1],"r",stdin);
freopen(argv[2],"w",stdout);
scanf("%d",&n);
for (k=0;1<<k*2<=n;++k);//此处需要微调
int p=tab[k];
for (int i=1;i<1<<k;++i){
int x=i,y=mod(mul(mod(mul(i,i),p),i),p);
ans[i]=x<<k|y;
// ans[i]=y<<k|x;
}
ans[0]=1<<k*2;
for (int i=0;i<1<<k;++i)
for (int j=0;j<i;++j)
if (bz[ans[i]^ans[j]]){
printf("-1\n");
return 0;
}
else
bz[ans[i]^ans[j]]=1;
int cnt=0;
for (int i=0;i<1<<k;++i)
if (ans[i]<=n)
cnt++;
printf("%d\n",cnt);
for (int i=0;i<1<<k;++i)
if (ans[i]<=n)
printf("%d\n",ans[i]);
return 0;
}