【CF Hello 2019】莫比乌斯反演+bitset

一道神奇的莫比乌斯反演+bitset CF HELLO2019f 题意:你有n(n 1e5) 个集合,每个集合中的数字都不超过7000。 操作1:将一个集合改为只有单独你赋给的一个数 操作2:将一个集合改为两个集合的并。 操作3:将一个集合改为两个集合两两gcd。 操作4:求某一个集合中某一个数的出现个数(mod 2) 看到这个mod 2 应该就瞬间想到bitset,但是如果bitset维护值域的话就没办法做第三个操作。 考虑莫比乌斯反演,我们维护集合F[x][y]表示集合x中y的倍数出现次数(y作为因数的原数个数)。 如果这样那么第一个操作赋值为Y就是$ F[d] = [d|Y] $ 第二个操作就是两个集合按位加(到二进制里就是异或) 第三个操作就是两个集合按位乘(到二进制里就是与) 第四个操作,由于有(f[n]表示原数列集合),反演一波 $ F[n] = \sum_{n|d}f[d] $ $ f[n] = \sum_{n|d}F[d]*\mu[d/n] $ 由于我们已经维护好了F数组,那么我们对每个数都预处理 $ \sum_{n|d}\mu[d/n] $(没有的地方直接补0),之后就和集合与一下就剩下的1个数%2就是答案了。
#include<stdio.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<bitset>

using namespace std;
bitset<7003>MU[7003],NUM[7003],sst[100005];
int mu[7003],pri[7003],cnt;
bool mk[7003];
int n,q;

void oula() {
    mk[1] = mk[0] = 1; mu[1] = 1;
    for(int i=2;i<=7000;i++) {
        if(!mk[i]) { pri[++cnt] = i; mu[i] = -1; }
        for(int j=1;j<=cnt&&1ll*i*pri[j]<=7000;j++) {
            int k = i*pri[j];
            mk[k] = 1;
            if(i%pri[j]==0) {
                mu[k] = 0;
                break;
            }
            mu[k] = -mu[i];
        }
    }
}

int main() {
    scanf("%d%d",&n,&q);
    oula();
    for(int i=1;i<=7000;i++) {
        for(int j=i;j<=7000;j+=i) {
            MU[i][j] = (mu[j/i]!=0);
            NUM[j][i] = 1;
        }
    }
    int op,x,y,z;
    for(int i=1;i<=q;i++) {
        scanf("%d",&op);
        if(op==1) {
            scanf("%d%d",&x,&y);
            sst[x] = NUM[y];
        } else if(op==2) {
            scanf("%d%d%d",&x,&y,&z);
            sst[x] = ( sst[y]^sst[z] );
        } else if(op==3) {
            scanf("%d%d%d",&x,&y,&z);
            sst[x] = ( sst[y]&sst[z] );
        } else {
            scanf("%d%d",&x,&y);
            int o = (sst[x]&MU[y]).count();
            printf("%d",o&1);
        }
    }
}
posted @ 2019-01-14 13:19  Newuser233  阅读(13)  评论(0)    收藏  举报