• 2022省选Day2T1解题报告


    2022省选Day2T1解题报告

    题目描述

    给定\(n\)个数\(a_i\),有\(m\)次询问,每次给定\(c_i\)个质数,你可以选出一些数满足选出的数的乘积可以被给定的\(c_i\)个质数整除,每次询问求有多少种选法

    \(n\leq 1e6~~a_i\leq 2000~\sum c_i \leq 18000\)

    题解

    当我们拿到一个询问选法的题时,大概率不是数学就是\(dp\)

    显然我们很容易看出需要容斥一手,但是发现如果直接容斥统计有点难

    考虑正难则反,我们可以处理有多少种取法不满足条件

    那么答案肯定就是\(2^n-res\) (\(res\)是不满足条件的选法数)

    我们先考虑部分分怎么做

    对于\(a_i<=30\)的情况,我们发现\(<30\)的质数只有10个

    那么答案肯定就是\(2^n\)减去某一个质数不选的方案数,再加上某两个质数不选的方案数,再减去某三个质数不选的方案数……

    然后考虑推广,我们发现对于一个数\(x\)最多只会有一个大于\(\sqrt x\)的质因数

    所以我们可以把大于\(\sqrt{x}\)的数单独处理

    而且对于两个大于\(\sqrt{x}\)的质数,是不可能同时不选也就是相对独立的

    所以我们的容斥可以拿\(>43\)的质数必须有的方案数去减

    这种做法可以当作套路去记一下,类似的题还有luogu2150 [NOI2015] 寿司晚宴

    那么对于小于\(\sqrt{x}\)的质数总共最多\(13\)个(\(43*47>2000\))

    所以我们考虑状压\(dp\)

    我们可以求出\(f_{i,j}\)表示状态为\(i\),且当前枚举的\(>43\)的质数为\(j\)的集合里的元素个数(\(i\)状态记录有哪几个质数不选)

    然后枚举\(>43\)的质数,把这些质数从集合中剔除,并将状态为\(i\)的答案乘上\(2^{cnt}-1\)(\(cnt\)指能被当前枚举的质数整除的数的个数)

    然后对于状态\(i\)\(1\)的奇偶性判断加或减就行了

    代码

    #include <ctime>
    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    #define int long long
    #define file(a) freopen(#a".in","r",stdin),freopen(#a".out","w",stdout)
    using namespace std;
    const int maxn=1e6+5,Mod=998244353;
    int n,a[maxn],m,cnt[2000+5],id[maxn],mp[2000+5];
    int f[(1<<13)+5][350],cnt1[(1<<13)+5],ans;
    bool is_prime[maxn];
    vector<int>prime,s[2000+5];
    void chkmax(int &x,int y) {if (x<y) x=y;}
    void chkmin(int &x,int y) {if (x>y) x=y;}
    int read() {
        int x=0,f=1;
        char ch=getchar();
        while(ch<'0' || ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while(ch<='9' && ch>='0') {x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return x*f;
    }
    int lowbit(int x) {return x&(-x);}
    int ksm(int a,int b) {
        int res=1;
        while(b) {
            if (b&1) (res*=a)%=Mod;
            b>>=1;(a*=a)%=Mod;
        }
        return res;
    }
    void init() {
        is_prime[1]=1;
        for (int i=2;i<=2000;i++) {
            if (!is_prime[i]) {
                id[i]=prime.size();
                prime.push_back(i);
            }
            for (size_t j=0;j<prime.size() && i*prime[j]<=2000;j++) {
                is_prime[i*prime[j]]=1;
                if (i%prime[j]==0) break;
            }
        }
        for (int i=2;i<=2000;i++) {
            for (int j=0;j<prime.size();j++) {
                if (i%prime[j]) continue;
                s[i].push_back(j);
                if (j<13) mp[i]|=(1<<j);
            }
        }
    }
    int calc(int x) {
        int res=0;
        while(x) {
            x-=lowbit(x);
            res++;
        }
        return res;
    }
    signed main() {
        file(luogu8292);
        n=read();
        for (int i=1;i<=n;i++) {
            a[i]=read();
            cnt[a[i]]++;
        }
        init();
        for (int i=0;i<(1<<13);i++) {
            for (int j=2;j<=2000;j++) {
                if (i&mp[j]) continue;
                f[i][s[j].back()]+=cnt[j];
                cnt1[i]+=cnt[j];
            }
        }
        int q=read();
        while(q--) {
            m=read();
            int tmp=0;ans=0;
            for (int i=1;i<=m;i++) {
                a[i]=read();
                if (id[a[i]]<13) tmp|=(1<<id[a[i]]);
            }
            // cout<<"tmp = "<<tmp<<endl;
            sort(a+1,a+1+m);a[m+1]=-1;
            for (int i=0;i<(1<<13);i++) {
                if ((tmp|i)>tmp) continue;
                int res=1,tot=cnt1[i];
                for (int j=1;j<=m;j++) {
                    if (a[j]==a[j+1]) continue;
                    if (id[a[j]]<13) continue;
                    (res*=(ksm(2,f[i][id[a[j]]])-1+Mod)%Mod)%=Mod;
                    tot-=f[i][id[a[j]]];
                }
                (res*=ksm(2,tot))%=Mod;
                if (calc(i)&1) ans=((ans-res)%Mod+Mod)%Mod;
                else ans=(ans+res)%Mod;
                // cout<<i<<' '<<tot<<endl;
            }
            cout<<ans<<endl;
        } 
        return 0;
    }
    /*
     ---
    /Y A\
    \___/   /
       \   /
        \ /*
    —————\ *
          \*____
          |*    \
          |*
          \*
           *
           *
    */
    
  • 相关阅读:
    Android代码宏控制方案 【转】
    android 系统签名【转】
    linux中udev简单的用法-->【转】
    linux中udev简单的用法【转】
    linux udev 机制【转】
    SQL SERVER2008 存储过程、表、视图、函数的权限
    用友u8数据库表结构
    SqlServer 添加用户 添加角色 分配权限
    SP_attach_db 添加数据库文件
    数据库的创建和文件的修改
  • 原文地址:https://www.cnblogs.com/xzj213/p/16195801.html
Copyright © 2020-2023  润新知