题面传送门

题意:

  • 你有一个集合 \(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. 华为在HDC2021发布全新HMS Core 6 宣布跨OS能力开放

    [2021年10月22日·东莞]华为开发者大会 2021(Together)于今天正式开幕,华为在主题演讲中正式发布全新的HMS Core 6,向全球开发者开放7大领域的69个Kit和21,738个A ...

  2. CentOS 用户与群组

    目录 1.用户管理 1.1.切换用户 1.2.添加用户 1.3.删除用户 1.4.修改用户 2.群组管理 2.1.查看群组 2.2.添加群组 2.3.删除群组 2.4.修改群组 1.用户管理 Linu ...

  3. 【数据结构与算法Python版学习笔记】图——拓扑排序 Topological Sort

    概念 很多问题都可转化为图, 利用图算法解决 例如早餐吃薄煎饼的过程 制作松饼的难点在于知道先做哪一步.从图7-18可知,可以首先加热平底锅或者混合原材料.我们借助拓扑排序这种图算法来确定制作松饼的步 ...

  4. the Agiles Scrum Meeting 6

    会议时间:2020.4.14 20:00 1.每个人的工作 今天已完成的工作 增量组:开发广播正文展开收起功能 issues:增量组:广播正文展开收起功能实现 完善组:修复冲刺部分的bug issue ...

  5. Netty:Netty中的零拷贝(Zero Copy)

    零复制概念: " 零复制"描述了计算机操作,其中CPU不执行将数据从一个存储区复制到另一个存储区的任务.通过网络传输文件时,通常用于节省CPU周期和内存带宽. WIKI的定义中,我 ...

  6. error: ‘int64_t’ does not name a type

    我在CodeBlock中编译工程没有出现问题,但是放到ubuntu上用自己写的Makefile make的时候报错 error: 'int64_t' does not name a type # 2 ...

  7. linux 内核源代码情景分析——linux 内存管理的基本框架

    386 CPU中的页式存管的基本思路是:通过页面目录和页面表分两个层次实现从线性地址到物理地址的映射.这种映射模式在大多数情况下可以节省页面表所占用的空间.因为大多数进程不会用到整个虚存空间,在虚存空 ...

  8. ACL实验

    ACL实验 基本配置:略 首先根据题目策略的需求1,从这个角度看,我们需要做一条高级ACL,因为我们不仅要看你是谁,还要看你去干什么事情,用高级ACL来做的话,对于我们华为设备,只写拒绝,因为华为默认 ...

  9. 【java+selenium3】线程休眠方法 (六)

    一.线程休眠的方法   Thread -- sleep 调用方式: Thread.sleep(long millis) 建议:不推荐使用此方式来等待,因为元素的实际渲染时间未知,长时间的等待则浪费的时 ...

  10. 端口被占用(启动tomcat时 错误: 代理抛出异常 : java.rmi.server.ExportException: Port already in use: 1099的解决办法)

    一.问题描述 在IntelliJ IDEA 中启动Tomcat服务器时就出现了如下图所示的错误: 错误: 代理抛出异常错误**: java.rmi.server.ExportException: Po ...