1. 程式人生 > 其它 >【AT3623】[ARC084D] XorShift(思維)

【AT3623】[ARC084D] XorShift(思維)

給定一個初始大小為 $n$ 的自然數集合 $\{a_1,a_2,\cdots a_n\}$,不斷進行兩種操作:若 $x$ 在集合中,則將 $2x$ 加入集合;若 $x,y$ 在集合中(允許 $x=y$),則將 $x\operatorname{xor}y$ 加入集合。求無限操作下去集合中將會有多少個小於等於 $m$ 的數。

題目連結

  • 給定一個初始大小為 \(n\) 的自然數集合 \(\{a_1,a_2,\cdots a_n\}\),不斷進行兩種操作:若 \(x\) 在集合中,則將 \(2x\) 加入集合;若 \(x,y\) 在集合中(允許 \(x=y\)),則將 \(x\operatorname{xor}y\) 加入集合。
  • 求無限操作下去集合中將會有多少個小於等於 \(m\) 的數。
  • \(n\le6\)\(0\le m,a_i\le 2^{4000}\)

所有數的公約數

眾所周知,異或可以視作二進位制下的不進位加法或不退位減法。

拋開不進位和不退位兩個限定詞,僅考慮可以進行加法或減法,那麼只要求出所有數的公約數 \(d\)

,顯然 \(d\) 的倍數都可以被表示出來。

現在只能進行左移和異或操作,只需要修改一下約數的定義即可。

考慮如何求兩個數 \(x,y\) 的公約數。不妨設 \(x\) 的最高位大於等於 \(y\) 的最高位,只需對 \(y\) 進行左移操作使它的最高位與 \(x\) 相同,然後用 \(x\) 異或這個數得到 \(x'\),則與 \(x\) 相比 \(x'\) 的最高位必然降低,然後遞迴求 \(y,x'\) 的公約數即可。

方便起見將上述給 \(x\) 異或 \(y\) 左移後的數的過程稱作 \(x\)\(y\) 取模(\(x\operatorname{mod}y\)),在之後還會用到。

這整個過程可以利用 bitset 優化。

答案的求解

假設公約數 \(s\) 的最高位為 \(h_s\)\(m\) 的最高位為 \(h_m\)

首先顯而易見的一點,若一個數 \(x\)\(s\) 的倍數,則當大於等於 \(h_s\) 的位確定後,小於 \(h_s\) 的位有唯一的填法。(設 \(x\) 保留大於等於 \(h_s\) 的位後的值為 \(x_0\),則小於 \(h_s\) 的位需要填入 \(x_0\operatorname{mod} s\),取模參考先前的定義)

那麼考慮第 \(h_s\sim h_m\) 位的填法,可以從高到低列舉第 \(i\) 位假設從這位開始不卡上界。如果這位為 \(0\)

則只能卡上界;如果這位為 \(1\) 則可以填 \(0\),若填 \(0\) 後面的位可以任填,第 \(h_s\sim i-1\) 位共有 \(2^{i-h_s}\) 種填法,且它們對應的小於 \(h_s\) 的位都必然能取到。

還有一種情況就是第 \(h_s\sim h_m\) 位全部卡上界,此時我們求出保留這部分後模 \(d\) 的值與 \(m\) 小於 \(h_s\) 的位比較,以確定這種情況是否有貢獻。

程式碼:\(O(\frac{nm^2}{\omega})\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 20
#define M 40000
#define X 998244353
using namespace std;
int n;struct INT {int h;bitset<M+1> o;}m,a,s,r;
char st[M+5];I void read(INT& S) {scanf("%s",st+1),S.o.reset(),S.h=strlen(st+1);for(RI i=1;i<=S.h;++i) st[i]&1&&(S.o.set(S.h-i+1),0);}
I INT operator % (INT x,INT y) {W(x.h>=y.h) {x.o^=y.o<<x.h-y.h;W(x.h&&!x.o.test(x.h)) --x.h;}return x;}//取模
I INT gcd(INT x,INT y) {if(!y.h) return x;if(x.h<y.h) return gcd(y,x);return Calc(y,x%y);}//求公約數
int main()
{
	RI i,t=0;for(scanf("%d",&n),read(m),i=1;i<=n;++i) read(a),s=gcd(s,a);
	RI p=1;for(i=s.h;i<=m.h;++i) m.o.test(i)&&(t=(t+p)%X),p=(p<<1)%X;for(r=m,i=1;i^s.h;++i) r.o.reset(i);//列舉每一位假設不卡上界;全卡上界,只留下第hs~hm位
	RI fg=1;for(r=r%s,i=s.h-1;i;--i) if(m.o.test(i)^r.o.test(i)) {r.o.test(i)&&(fg=0);break;}return printf("%d\n",(t+fg)%X),0;//判斷全卡上界時是否小於等於m
}
敗得義無反顧,弱得一無是處