【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;
}
posted @ 2021-02-27 17:15  jz_597  阅读(150)  评论(0编辑  收藏  举报