• 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;
    }
    
  • 相关阅读:
    linux 学习之七-部分ssh命令
    揭秘淘宝286亿海量图片存储与处理架构
    大型网站后台架构的演变
    Linux学习之六-Yum命令的使用
    res://ieframe.dll/acr_error.htm 纯手动解决方法
    VS2013启动项目调试的时候会启动本地IIS
    linux学习之(六)-主机名、网络IP的配置与查看
    deployment.yaml 带同步时区
    Deployment
    mysql.yaml
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/14853873.html
Copyright © 2020-2023  润新知