ARC084F XorShift【多项式】【位运算】

ARC084F XorShift

Description

\(n\) 个二进制数 \(a_i\),你可以将场上的任意两个二进制数异或起来得到一个新的数,也可以将某个二进制数乘 \(2\) 得到新的二进制数。请问你能最多能得到多少个 \(\le Lim\) 的数。 \(n\le 6,a_i,Lim\le 2^{4000}\),可加强至 \(\le 2^{20000}\)

Solution

这已经不是第一次遇到二进制数的异或以及左移这样的问题了,也并不是第一次遇到将异或转化为 \(\mathbb{F}_2[x]\) 域下的多项式加法(上次出现是 这里的 恋歌,但我却依然没有及时想到这种思路,希望下次能够记住这一点。

可以将题目种的一个二进制数转化为 \(\mathbb{F}_2[x]\) 域下的多项式 \(f(x)=\sum c_ix^i\) 其中 \(c_i\) 为该二进制数从低到高第 \(i\) 位,那么异或就是多项式加法,乘 \(2\) 就是让 \(f(x)\cdot x\)

既然操作只有加法与乘法,那么设 \(D(x)\) 为所有二进制数的最大公因式,显然无论如何操作,最终得到的数都得是 \(D(x)\) 的倍数。不仅如此,我们可以进一步说明,所有 \(D(x)\) 的倍数都可以被表示出来。

如果我们能够通过操作得到 \(D(x)\),那么可以容易的得到所有 \(D(x)\) 的倍数。考虑如何得到 \(D(x)\)。首先求出两个多项式的 \(gcd\),再与第三个多项式求 \(gcd\),如此反复即可得到 \(D(x)\)

求两个多项式的 \(gcd\) 时,考虑经典的辗转相减法。注意异或即可被看作多项式加法,也可被看作多项式减法,因此直接实现辗转相减法,即可得到 \(D(x)\)。同时,实现代码时也可以通过辗转相减法得到 \(D(x)\) (当然是选择辗转相除法,多项式的取模是可以通过 \(\mathcal O(\dfrac{d^2}{\omega})\) 完成的)。

现在问题转化为了求出所有 \(<Lim\)\(D(x)\) 倍数的个数,有一个结论:如果一个多项式除最低 \(deg(D)-1\) 位的数均已确定,那么符合这样条件的多项式中只有唯一一个 \(D(x)\) 的倍数。证明也是容易的,直接用该多项式对 \(D(x)\) 取模,就会得到最终 \(deg(D)-1\) 位的数字。

因此考虑枚举该 \(D(x)\) 的倍数是在第几位处开始 \(<Lim\) 的(前面都与 \(Lim\) 相等)。不妨设为第 \(l\) 位,那么还有 \(m=deg(Lim)-l-deg(D)+1\) 项未被确定,若 \(m>deg(D)-1\) ,那么还有 \(m-deg(D)+1\) 位可随意选择,共 \(2^{m-deg(D)+1}\) 位;否则,后 \(deg(D)-1\) 位也已确定,只需检查这一个数是否 \(\le Lim\) 即可。

最终我们只需要实现 \(n\) 次求 \(gcd\),一次多项式取模,总复杂度为 \(\mathcal O(\dfrac{nd^2\log(d)}{\omega})\)

Code

#include<bits/stdc++.h>
using namespace std;
const int mod=73946381,N=20001;
char s[N];int pw[N],n;
struct poly{
	int d;
	bitset<N> v;
	poly(){d=0;v.reset();}
	inline bool operator [](int x){return v.test(x);} 
	inline void set(int x){v.set(x);}
	inline void shrink(){while(d>0&&!v[d-1])d--;}
	inline poly operator %(const poly &g)const{
		poly ret=*this;
		for(int i=d-1;i>=g.d-1;--i) 
			if(ret[i]) ret.v^=g.v<<(i-g.d+1);
		ret.d=g.d-1;ret.shrink();
		return ret;
	}
	inline void read(){
		scanf("%s",s+1);int len=strlen(s+1);d=len;
		for(int i=0,j=len;j;i++,j--) if(s[j]=='1') v.set(i);
	}
	inline void print(){
		printf("deg is%d\n",d);
		for(int i=0;i<d;++i) printf("%d ",v.test(i));puts("");
	}
}f,g,a[6];
inline poly gcd(poly f,poly g){
	int n=f.d,m=g.d;
	if(n<m) swap(n,m),swap(f,g);
	for(;g.d;swap(f,g)) f=f%g;
	return f;
}
inline int add(int x,int y){return (x+y>=mod)?x+y-mod:x+y;}
inline void inc(int &x,int y){x=(x+y>=mod)?x+y-mod:x+y;}
int main(){
	scanf("%d",&n);
	f.read();
	for(int i=0;i<n;++i){
		a[i].read();
		if(i==0) g=a[i];
		else g=gcd(g,a[i]);
	}
	int x=f.d,y=g.d,ans=0;
	pw[0]=1;
	for(int i=1;i<max(x,y);++i) pw[i]=add(pw[i-1],pw[i-1]);
	for(int i=x-1;i>=y-1;--i) if(f[i]) inc(ans,pw[i-y+1]);
	poly tmp=f;
	for(int i=0;i<y-1;++i) tmp.v[i]=0;
	poly h=tmp%g;
	for(int j=y-2;j>=0;--j){
		if(h[j]<f[j]) break;
		else if(h[j]>f[j]){ans--;break;}
	}
	ans++;
	printf("%d\n",ans%mod);
	return 0;
}
posted @ 2021-06-05 21:07  cjTQX  阅读(155)  评论(0编辑  收藏  举报