- 有 \(n\) 个初始为空的可重集。
- \(q\) 次操作,分为四种:
- 将第 \(x\) 个集合赋值为 \(\{v\}\)。
- 令第 \(x\) 个集合为第 \(y\) 个集合与第 \(z\) 个集合的并。
- 令第 \(x\) 个集合为第 \(y\) 个集合与第 \(z\) 个集合的积,定义 \(A*B=\{\gcd(a,b)|a\in A,b\in B\}\)。
- 询问 \(v\) 在第 \(x\) 个集合中出现次数的奇偶性。
- \(1\le n\le10^5\),\(1\le q\le10^6\),\(1\le v\le 7000\)
维护约数出现次数
注意到这里集合的积定义比较奇怪,如果只是单纯维护原集合似乎不是很方便。
所以考虑直接维护每个约数的出现次数。
对于并,显然就是将约数出现次数直接相加。
对于积,容易发现就是将约数出现次数直接相乘。
然后对于 \(v\) 在第 \(x\) 个集合中的出现次数,列出式子:
\[\begin{aligned}
\sum_{a\in S_x}[a=v]&=\sum_{a\in S_x}[v|a][\frac av=1]\\
&=\sum_{a\in S_x}[v|a]\sum_{d|\frac av}\mu(d)\\
&=\sum_{d=1}^{\lfloor\frac Vv\rfloor}\mu(d)\sum_{a\in S_x}[dv|a]
\end{aligned}
\]
发现 \(\sum\) 中的式子就是 \(dv\) 作为约数的出现次数,正好是我们维护的值。
bitset
本题一个特殊的地方就是只需要求奇偶性,所以考虑 bitset。
对于并,二进制下不进位相加显然就是异或。
对于积,相乘就是按位与。
而对于询问,我们对于 \(v\) 预处理一个 bitset,其中第 \(dv\) 位的值为 \(\mu(d)\operatorname{mod}2\)。那么这又是一个相乘的过程,只需要按位与即可。
代码:\(O(\frac{qv}{\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 100000
#define V 7000
using namespace std;
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
char oc,FI[FS],*FA=FI,*FB=FI;
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int n,Pt,P[V+5],Mu[V+5];bitset<V+5> a[N+5],v[N+5],g[N+5];
int main()
{
RI Qt,i,j,op,x,y,z;read(n,Qt);
for(Mu[1]=1,i=2;i<=V;++i) for(!P[i]&&(Mu[P[++Pt]=i]=-1),j=1;i*P[j]<=V;++j) if(P[i*P[j]]=1,i%P[j]) Mu[i*P[j]]=-Mu[i];else break;//线性筛
for(i=1;i<=V;++i) for(j=i;j<=V;j+=i) v[j].set(i),Mu[j/i]&&(g[i].set(j),0);//预处理bitset
W(Qt--) switch(read(op,x,y),op)
{
case 1:a[x]=v[y];break;case 2:read(z),a[x]=a[y]^a[z];break;case 3:read(z),a[x]=a[y]&a[z];break;//赋值;并;积
case 4:putchar(48|(a[x]&g[y]).count()&1);break;//询问
}return 0;
}