由于只要考虑 $\mod 2$ 意义下的答案,所以我们只要维护一堆的 $01$
容易想到用 $bitset$ 瞎搞...,发现当复杂度 $qv/32$ 是可以过的...
一开始容易想到对每个集合开一个 $bitset$ ,叫 $cnt[]$ ,维护各种值的数出现了奇数还是偶数次
因为要维护那个奇怪的 $3$ 操作,所以改成维护各种值的倍数出现了奇数还是偶数次,即
$cnt[x]$ 维护集合内所有 $x|d$ 的数 $d$ 的出现次数
那么对于操作 $3$,$x$ 的倍数和 $y$ 的倍数相乘后 $x$ 的倍数和 $y$ 的倍数数量都是 $cnt[x] \cdot cnt[y]$
然后就可以很容易维护了,因为只有 $0,1$ 那么其实相当于把两个 $bitset$ 取 $\text{'&'}$ 即可
同时对于操作 $2$ ,显然只要对 $bitset$ 取 $\text{'^'}$ 就行
然后操作 $1$ ,直接把 $bitset$ 清空,然后设集合内的数为 $d$ ,那么直接根号筛一下 $d$ 的因数 $x$ 然后 $cnt[x]=1$ 即可
最后是操作 $4$ ,因为我们维护的是 $x$ 的倍数的出现次数,设 $F(x),f(x)$ 分别为 $x$ 倍数出现次数,$x$ 出现次数
那么有 $F(x)=\sum_{x|d} f(d)$ ,然后就发现了熟悉的莫比乌斯反演,我们知道 $F$ 想求 $f$,直接反演可得
$f(x)=\sum_{x|d} \mu (\frac{d}{x}) F(d)$ ,由于 $\mod 2$ 意义下 $-1 \equiv 1$ 所以可以用 $bitset$ 维护一下每个 $x$ 的所有 $x|d$ 的 $\mu(d/x)$
即设 $bitset$ $g[x]$ 维护一下 $x|d$ 的 $g[x][d]=\mu(d/x)$ 然后对于 $4$ 操作 $(4\ x\ v)$ 就是 $(cnt[x]&g[v]).count()&1$
代码不长
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<bitset> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e5+7,M=7007; int n,Q; int pri[M],mu[M],tot; bool not_pri[M]; bitset <M> cnt[N],g[M]; void init() { not_pri[1]=1; mu[1]=1; for(int i=2;i<M;i++) { if(!not_pri[i]) pri[++tot]=i,mu[i]=1; for(int j=1;j<=tot;j++) { ll g=1ll*i*pri[j]; if(g>=M) break; not_pri[g]=1; if(i%pri[j]==0) break; mu[g]=-mu[i]; } } for(int i=1;i<M;i++) for(int j=i;j<M;j+=i) g[i][j]=abs(mu[j/i]); } int main() { n=read(),Q=read(); init(); int a,b,c,d; while(Q--) { a=read(),b=read(),c=read(); if(a==1) { cnt[b]=0; int T=sqrt(c); for(int i=1;i<=T;i++) if(c%i==0) cnt[b][i]=cnt[b][c/i]=1; } else if(a==2) d=read(),cnt[b]=cnt[c]^cnt[d]; else if(a==3) d=read(),cnt[b]=cnt[c]&cnt[d]; else printf("%d",int((cnt[b]&g[c]).count())&1); } puts(""); return 0; }