CF1097F Alex and a TV Show 莫比乌斯反演、bitset

传送门


发现自己对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;
}
posted @ 2019-01-07 20:15  cjoier_Itst  阅读(341)  评论(0编辑  收藏  举报