AT3623 [ARC084D] XorShift
题面传送门
写了个线性基暴力插入结果又T又WA
首先我们可以把这个东西看成二进制多项式。
然后这个二进制多项式的两个操作可以看成加减和位移。
我们考虑处理出\(G=gcd(a_i)\),那么可以证明是\(G\)的倍数是能被表示出来的充要条件。
因为\(a\)都是\(G\)的倍数而加减乘除并不影响这个。
处理\(G\)可以用辗转相除法解决。
然后我们又可以知道如果我们空着了一个多项式的后\(|G|-1\)位那么一定可以构造出一个多项式使得这个多项式是\(G\)的倍数。
具体的,设已经填好的多项式为\(S\),那么在后\(|G|-1\)位中填上\(S\bmod G\)即可。
再考虑小于\(lim\)怎么处理。
采用数位dp经典方法就可以枚举前面固定的然后计算答案。
最后一位特判一下看填上去是否小于\(G\)再加上即可。
以上这些都可以bitset优化,时间复杂度\(O(\frac{nm^2}{w})\)
code:
#include <vector>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<set>
#include<map>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db long double
#define N 6
#define M 20000
#define eps (1e-14)
#define mod 998244353
#define U unsigned
using namespace std;
int n,m,flag;ll po[M+5],ans;
struct pai{bitset<M+5> a;int len=M;I void Getlen(){while(~len&&!a[len]) len--;}}f[N+5],G,lim,pus,now;
I pai Bmod(pai A,pai B){
while(A.len>=B.len)A.a^=B.a<<A.len-B.len,A.Getlen();return A;
}
I pai Gcd(pai A,pai B){return B.a.any()?Gcd(B,Bmod(A,B)):A;}
int main(){
freopen("cult.in","r",stdin);freopen("cult.out","w",stdout);
re int i;scanf("%d",&n);cin>>lim.a;lim.Getlen();for(i=1;i<=n;i++) cin>>f[i].a,f[i].Getlen();
G=f[1];for(i=2;i<=n;i++) G=Gcd(G,f[i]);po[0]=1;for(i=1;i<=M;i++) po[i]=po[i-1]*2%mod;
for(i=lim.len;i>=G.len;i--)lim.a[i]&&(ans+=po[i-G.len]);for(i=lim.len;i>=G.len;i--) now.a[i]=lim.a[i];now.len=lim.len;
pus=Bmod(now,G);ans++;for(i=G.len-1;~i;i--)if(lim.a[i]!=pus.a[i]) {ans-=pus.a[i]>lim.a[i];break;}printf("%lld\n",ans%mod);
}