发现自己对mobius反演的理解比较浅显……
首先我们只需要维护每一个数的出现次数(mod 2)的值,那么实际上我们只需要使用(bitset)进行维护,每一次加入一个数将其对应次数异或(1)。那么(2)操作就相当于将集合(x)对应的(bitset)赋值为(y)与(z)的异或和。
看到(3)操作中的gcd,考虑莫比乌斯反演。我们在加入一个数到集合中的时候,不是加入它本身,而是加入它的所有因子。这样我们的(3)操作的实质就是一个按位与操作了。
对于(1)操作,我们可以预处理所有数对应的要加入到集合中的数,可以(O(d^2))解决
对于(4)操作,可以发现(1 equiv -1 mod 2),所以我们不需要关注莫比乌斯函数的正负,只需要关注是否有值即可。我们同样使用(bitset)预处理询问中哪一些数可以对当前询问的数产生贡献,查询时按位与当前查询的集合就可以得到我们想要的答案了。
#include<bits/stdc++.h>
//This code is written by Itst
using namespace std;
inline int read(){
int a = 0;
char c = getchar();
while(!isdigit(c) && c != EOF)
c = getchar();
if(c == EOF)
exit(0);
while(isdigit(c)){
a = a * 10 + c - 48;
c = getchar();
}
return a;
}
bitset < 7010 > from[7010] , to[7010] , mset[100010];
int N , Q;
bool vis[7010];
int main(){
N = read();
Q = read();
for(int i = 2 ; i * i <= 7000 ; ++i)
for(int j = 1 ; j * i * i <= 7000 ; ++j)
vis[i * i * j] = 1;
for(int i = 1 ; i <= 7000 ; ++i){
for(int j = 1 ; j * i <= 7000 ; ++j){
from[i * j].set(i);
if(!vis[j])
to[i].set(i * j);
}
}
int a , b , c;
for(int i = 1 ; i <= Q ; ++i)
switch(read()){
case 1:
a = read() , b = read();
mset[a] = from[b];
break;
case 2:
a = read() , b = read() , c = read();
mset[a] = mset[b] ^ mset[c];
break;
case 3:
a = read() , b = read() , c = read();
mset[a] = mset[b] & mset[c];
break;
case 4:
a = read() , b = read();
printf("%d" , (mset[a] & to[b]).count() & 1);
}
return 0;
}