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

ARC084F XorShift

Description

(n) 个二进制数 (a_i),你可以将场上的任意两个二进制数异或起来得到一个新的数,也可以将某个二进制数乘 (2) 得到新的二进制数。请问你能最多能得到多少个 (le Lim) 的数。 (nle 6,a_i,Limle 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^2log(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
",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
",ans%mod);
	return 0;
}
原文地址:https://www.cnblogs.com/tqxboomzero/p/14853873.html