题面传送门

题意:

  • 你有一个集合 \(S={2,3,\dots,n}\)
  • 你要选择两个集合 \(A\) 和 \(B\),满足:
  1. \(A \subseteq S\),\(B \subseteq S\),且 \(A \cap B=\varnothing\)
  2. 不存在两个数 \(x \in A\),\(y \in B\),且 \(\operatorname{gcd}(x,y)>1\)。
  • 求满足条件的集合 \(A,B\) 的数量。

    \(n \in [2,500]\)

通过分析题面可以发现,如果两个集合 \(A,B\) 满足条件,那么它们的质因子组成的集合一定不会重复。

先考虑 \(30\) 分的做法。\(30\) 以内的质数只有 \(10\) 个(\(2,3,5,7,11,13,17,19,23,29\))。

我们分别给这 \(10\) 个质数标号 \(1\) 到 \(10\)。这样我们可以使用一个 \(10\) 位二进制数表示质因子的选择情况。

例如 \((1011001100)_2\) 就表示集合中包含质因子 \(2,5,7,17,19\)。

再将 \(2\) 到 \(n\) 每个数分解质因数。记 \(fac_i\) 为 \(i\) 质因子的集合。

到这里,不少题解都使用了三维状压 \(dp\)。这里我要介绍一个不太一样的方法。

我们开一个数组 \(cnt_{i,j}\),其中 \(i \in [2,n],j \in [0,2^{10}-1]\)。

表示满足 \(2 \leq x \leq i\),\(fac_i \subseteq j\) 的 \(x\) 的个数

再设 \(dp_s\) 表示质因子集合为 \(s\) 的集合数量。

显然所有 \(fac_i \subseteq s\) 的 \(i\) 都可以被选,那么总共有 \(cnt_{n,i}\) 个数,每个数都有选与不选两种可能,故 \(dp_s=2^{cnt_{n,i}}\)

最后是统计答案,枚举质因子集合 \(s,t\)(根据条件 2,\(s\&t=0\)),两个集合的选择方法是独立的,因此,根据乘法原理,这一部分对答案的贡献为 \(dp_s \times dp_t\)。

但是这样做还不行。假设有一个集合 \(T={2,4}\),那么 \(T\) 在 \(dp_1\) 中被计算了一次,在 \(dp_3\) 中又被计算了一次,会被重复计算。

这时不难想到容斥原理。你对 \(dp\) 数组进行一遍容斥原理就可以了。

再考虑正解。\(n\) 从 \(30\) 扩大到了 \(500\),就不能像 \(30\) 分做法那样将一个质因子集合用二进制表示出来了。

但是注意到 \(\sqrt{500}\) 只有 \(22\),而 \(22\) 以内的质数只有 \(8\) 个。

那么我们可以用根号分治的思想将质数分成两类,一类是 \(\leq 22\) 的质数(我们称其为“小质数”),另一类是 \(\gt 22\) 的质数(我们称其为“大质数”)。

不难发现,\(1\) 到 \(500\) 中的数最多只有一个大质数因子。任意两个大质数乘起来都会 \(\gt 500\)。

换句话说,大质数的选择方案是独立的(是否选择 \(31\) 这个质因子不会影响 \(37\) 的选择方案)

我们枚举小质数因子的集合 \(s,t\),然后考虑每个大质数 \(p\) 的选择方法:

  1. \(p\) 在 \(A\) 中,那么选择的方案数为 \(2^{cnt_{\frac{n}{p},s}+1}\)(你可以看成从 \([1,\frac{n}{p}]\) 选择若干个质因子集合为 \(s\) 的集合然后将每个数乘以 \(p\))
  2. \(p\) 在 \(B\) 中,那么选择的方案数为 \(2^{cnt_{\frac{n}{p},t}+1}\)
  3. \(p\) 既不在 \(A\) 也不在 \(B\) 中,方案数为 \(1\)。

    将三者加起来就是选择 \(p\) 的方案数。

    将选择每个大质数的方案数乘起来就是选择大质数的方案数。

    选择小质数的方案数和 \(30\) 分做法一样用乘法原理将两部分乘起来。

    用容斥原理算一下就可以了。
/*
Contest: -
Problem: P2150
Author: tzc_wk
Time: 2020.7.31
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a) a.begin(),a.end()
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,0x3f,sizeof(a))
#define fillsmall(a) memset(a,0xcf,sizeof(a))
#define y1 y1010101010101
#define y0 y0101010101010
#define int long long
typedef pair<int,int> pii;
inline int read(){
int x=0,neg=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*neg;
}
int n=read(),m=read();
inline int qpow(int x,int e){
int ans=1;
while(e){
if(e&1) ans=ans*x%m;
x=x*x%m;
e>>=1;
}
return ans;
}
int pr[505],num[505],pcnt=0;
bool vis[505];
inline void sieve(){
for(int i=2;i<=500;i++){
if(!vis[i]){
pr[++pcnt]=i;num[i]=pcnt;
for(int j=i+i;j<=500;j+=i) vis[j]=1;
}
}
}
int sumfac[505][1<<8];
int g[505][1<<8],gg[505][1<<8];
inline int calc_fac(int x){
int tmp=x,flg=0;
for(int i=2;i*i<=x;i++){
if(tmp%i==0){
flg|=(1<<(num[i]-1));
while(tmp%i==0) tmp/=i;
}
}
if(tmp>1){
if(num[tmp]>8) return 256;
else flg|=(1<<(num[tmp]-1));
}
return flg;
}
signed main(){
sieve();
fz(i,2,n){
int f=calc_fac(i);
for(int j=0;j<(1<<8);j++){
if((j&f)!=f) sumfac[i][j]=sumfac[i-1][j];
else sumfac[i][j]=sumfac[i-1][j]+1;
// cout<<i<<" "<<j<<" "<<sumfac[i][j]<<endl;
}
}
for(int i=0;i<(1<<8);i++) for(int j=0;j<(1<<8);j++){
if((i&j)==0){
// if(dp[i][n]==0||dp[j][n]==0) continue;
// cout<<i<<" "<<j<<" "<<dp[i][n]<<" "<<dp[j][n]<<endl;
int ans=1;
ans=ans*qpow(2,sumfac[n][i])%m;
ans=ans*qpow(2,sumfac[n][j])%m;
for(int k=23;k<=n;k++){
if(num[k]){
// if(k==23) cout<<i<<" "<<j<<" "<<qpow(2,sumfac[n/k][i]+1)+qpow(2,sumfac[n/k][j]+1)-1<<endl;
ans=ans*(qpow(2,sumfac[n/k][i]+1)+qpow(2,sumfac[n/k][j]+1)-1+m)%m;
}
}
g[i][j]=ans;
// cout<<i<<" "<<j<<" "<<g[i][j]<<endl;
}
}
for(int i=0;i<(1<<8);i++) for(int j=0;j<(1<<8);j++){
if((i&j)==0){
for(int k=0;k<(1<<8);k++){
if((j&k)!=k) continue;
gg[i][j]=(gg[i][j]+(int)pow(-1,__builtin_popcount(j^k))*g[i][k]+m)%m;
}
// cout<<i<<" "<<j<<" "<<gg[i][j]<<endl;
}
}
int sum=0;
for(int i=0;i<(1<<8);i++) for(int j=0;j<(1<<8);j++){
if((i&j)==0){
int x=0;
for(int k=0;k<(1<<8);k++){
if((i&k)!=k) continue;
x=(x+(int)pow(-1,__builtin_popcount(i^k))*gg[k][j]+m)%m;
}
sum=(sum+x)%m;
// cout<<i<<" "<<j<<" "<<x<<endl;
}
}
cout<<sum<<endl;
return 0;
}

UOJ #129 / BZOJ 4197 / 洛谷 P2150 - [NOI2015]寿司晚宴 (状压dp+数论+容斥)的更多相关文章

  1. [NOI2015]寿司晚宴 --- 状压DP

    [NOI2015]寿司晚宴 题目描述 为了庆祝NOI的成功开幕,主办方为大家准备了一场寿司晚宴. 小G和小W作为参加NOI的选手,也被邀请参加了寿司晚宴. 在晚宴上,主办方为大家提供了n−1种不同的寿 ...

  2. 【BZOJ4197】[Noi2015]寿司晚宴 状压DP+分解质因数

    [BZOJ4197][Noi2015]寿司晚宴 Description 为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴.小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴 ...

  3. bzoj4197 [Noi2015]寿司晚宴——状压DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4197 首先,两个人选的数都互质可以看作是一个人选了一个数,就相当于选了一个质因数集合,另一个 ...

  4. 洛谷$P2150\ [NOI2015]$寿司晚宴 $dp$

    正解:$dp$ 解题报告: 传送门$QwQ$. 遇事不决写$dp$($bushi$.讲道理这题一看就感觉除了$dp$也没啥很好的算法能做了,于是考虑$dp$呗 先看部分分?$30pts$发现质因数个数 ...

  5. BZOJ 4197: [Noi2015]寿司晚宴 状压dp+质因数分解

    挺神的一道题 ~ 由于两个人选的数字不能有互质的情况,所以说对于一个质因子来说,如果 1 选了,则 2 不能选任何整除该质因子的数. 然后,我们发现对于 1 ~ 500 的数字来说,只可能有一个大于 ...

  6. [NOI2015]寿司晚宴——状压dp

    题目转化:将2~n的数分成两组,可以不选,使得这两组没有公共的质因子.求方案数. 选择了一个数,相当于选择了它的所有质因子. 30分: 发现,n<=30的时候,涉及到的质因子也就10个.2,3, ...

  7. B4197 [Noi2015]寿司晚宴 状压dp

    这个题一开始想到了唯一分解定理,然后状压.但是显然数组开不下,后来想到每个数(n<500)大于19的素因子只可能有一个,所以直接单独存就行了. 然后正常状压dp就很好搞了. 题干: Descri ...

  8. BZOJ 4197 NOI 2015 寿司晚宴 状压DP

    4197: [Noi2015]寿司晚宴 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 694  Solved: 440[Submit][Status] ...

  9. 【题解】洛谷P3959 [NOIP2017TG] 宝藏(状压DP+DFS)

    洛谷P3959:https://www.luogu.org/problemnew/show/P3959 前言 NOIP2017时还很弱(现在也很弱 看出来是DP 但是并不会状压DP 现在看来思路并不复 ...

随机推荐

  1. 分布式表示(Distributed Representation)

    NLP模型笔记 - 分布式表示 ziuno 2020-03-08 19:52:50 410 收藏 2 分类专栏: NLP 模型 笔记 文章标签: nlp 最后发布:2020-03-08 19:52:5 ...

  2. Sequence Model-week2编程题1-词向量的操作【余弦相似度 词类比 除偏词向量】

    1. 词向量上的操作(Operations on word vectors) 因为词嵌入的训练是非常耗资源的,所以ML从业者通常 都是 选择加载训练好 的 词嵌入(Embedding)数据集.(不用自 ...

  3. Java:内部类小记

    Java:内部类小记 对 Java 中的 内部类,做一个微不足道的小小小小记 首先:内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的. 成员内部类 成员内 ...

  4. 异常大讨论-抛出异常还是返回false

    iteye精华帖之异常大讨论 原帖链接http://www.iteye.com/topic/2038 Robbin的观点 观点1:Exception实际上代表了一个UseCase中的异常流的处理. 绝 ...

  5. 零基础入门必备的Linux命令和C语言基础

    文件和目录(底部有视频资料) cd /home 进入 '/ home' 目录' cd - 返回上一级目录 cd -/- 返回上两级目录 cd 进入个人的主目录 cd ~user1 进入个人的主目录 c ...

  6. Java I/O框架 - 总结概述

    总结 以下需要重点掌握: 字节流,以下读取结束全部返回-1 字节节点流-访问文件 FileInputStream/FileOutputStream 可以读取任意文件 可以复制图片 读取字符String ...

  7. 极速上手 VUE 3—v-model 的使用变化

    本篇文章主要介绍 v-model 在 Vue2 和 Vue3 中使用变化. 一.Vue2 中 v-model 的使用 v-model 是语法糖,本质还是父子组件间的通信.父子组件通信时有两种方式: 父 ...

  8. 关于axios 的responseType类型的设置

    responseType值的类型可为如下 axios请求下载导出一个文件,请求成功时返回的是一个流形式的文件,需要设置responseType: 'arraybuffer',但是请求失败的需要返回的是 ...

  9. Centos 8 升级ssl到1.1.1h

    升级到1.1.1h版本 #编译openssl和安装 ./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl & ...

  10. pvcreate vgcreate lvcreate 扩容

    centos6 服务器磁盘扩容 1.创建物理卷 /dev/sdb #pvcreate /dev/sdb 参数:/dev/sdb 设备名 2.创建卷组 vg_02 #vgcreate  vg_02  / ...