[NOI2015][bzoj4197] 寿司晚宴 [状压dp+质因数]
题面
思路
首先,要让两个人选的数字全部互质,那么有一个显然的充要条件:甲选的数字的质因数集合和乙选的数字的质因数集合没有交集
30pt
这种情况下n<=30,也就是说可用的质数只有10个,我们可以开个状压搞一搞
设$dp[S_1][S_2]$表示甲选择的质因数集合是$S_1$,乙是$S_2$的总情况数,
对于每个2-n分解质因数,把每个质因数是否出现状压起来存下来,dp的时候从前往后扫
那么可以刷表法做一波$dp[i][S_1|k][S_2]+=dp[i-1][S_1][S_2]$$(k$ $bitand$ $S_2=0)$,或者$dp[i][S_1][S_2|k]+=dp[i-1][S_1][S_2]$$(k$ $bitand$ $S_1=0)$,其中k是当前数的质因数集合,$bitand$是位与
滚动数组优化一下,把第一维去掉,总效率是$O(2^{20}n)$
100pt
这时有什么变化了呢?没错,n到了500以后可用的质因数变多了,我们无法把它们全部都压进一个数里面了
注意到,一个小于500的数,最多只可能有1个比22大的质因子
所以我们可以把这个质因子单独拿出来记录一下(没有就记为0)
然后,我们把2-n这些数按照大质因子大小排序,这样令大质因子相同的数排在一起(也就是不能甲乙同时选的)
我们记录三个相同数组:$dp[S_1][S_2],f1[][],f2[][]$,因为小质因数只有8个,所以$0\leq S_1,S_2\leq 255$
对于每一段大质因子相同的数,我们在这一段开始的时候把dp的值赋给f1和f2,然后在这一段内部用刷表法推f1和f2
具体来说这么做
$f1[i][S_1|k][S_2]+=f1[i-1][S_1][S_2]$$(k$ $bitand$ $S_2=0)$
或者$f2[i][S_1][S_2|k]+=f2[i-1][S_1][S_2]$$(k$ $bitand$ $S_1=0)$
其中k是当前数的小质因数集合(只有8个二进制位了)
大家应当看出来了,f1表示的就是这个大质因子让第一个人选,f2就是这个大质因子让第二个人选
这一段数推完以后,再把f1f2合并到dp里面,$dp[S_1][S_2]=f1[S_1][S_2]+f2[S_1][S_2]-dp[S_1][S_2]$
这里减掉一个dp是因为两种情况会重复统计两个人都不选的情况(也就是原来的dp[S_1][S_2]的值),减掉即可
最后答案就是$dp[0-255][0-255]$的和了
总时间复杂度为$O(n2^{16})$(居然比30pt做法还快【滑稽】)
Code:
详细的实现见代码
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
inline int read(){
int re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int n,MOD;
int p[10]={0,2,3,5,7,11,13,17,19,0};
struct node{
int val,big,S;//big就是大质因数,S是小质因数集合
void init(){
int i,tmp=val;big=-1;
for(i=1;i<=8;i++){//分解质因数
if(tmp%p[i]) continue;
S|=(1<<i-1);
while(tmp%p[i]==0) tmp/=p[i];
}
if(tmp!=1) big=tmp;
}
}a[510];
inline bool cmp(node l,node r){
return l.big<r.big;
}
int pl(int l,int r){
l+=r;
return l>=MOD?l-MOD:l;
}
int dp[300][300],f1[300][300],f2[300][300];
int main(){
n=read();MOD=read();int i,j,k;
for(i=2;i<=n;i++) a[i-1].val=i,a[i-1].init();
sort(a+1,a+n,cmp);
dp[0][0]=1;
for(i=1;i<n;i++){
if(i==1||a[i].big!=a[i-1].big||a[i].big==-1){
memcpy(f1,dp,sizeof(f1));
memcpy(f2,dp,sizeof(f2));
}
for(j=255;j>=0;j--){
for(k=255;k>=0;k--){//因为是滚动数组,所以一定要倒着推
if(j&k) continue;
if((a[i].S&j)==0) f2[j][k|a[i].S]=pl(f2[j][k|a[i].S],f2[j][k]);
if((a[i].S&k)==0) f1[j|a[i].S][k]=pl(f1[j|a[i].S][k],f1[j][k]);
}
}
if(i==n-1||a[i].big!=a[i+1].big||a[i].big==-1){
for(j=0;j<=255;j++){
for(k=0;k<=255;k++){
if(j&k) continue;
dp[j][k]=pl(f1[j][k],pl(f2[j][k],MOD-dp[j][k]));
}
}
}
}
ll ans=0;
for(j=0;j<=255;j++){
for(k=0;k<=255;k++){
if((j&k)==0&&dp[j][k]) ans=pl(ans,dp[j][k]);
}
}
cout<<ans;
}
[NOI2015][bzoj4197] 寿司晚宴 [状压dp+质因数]的更多相关文章
- BZOJ 4197: [Noi2015]寿司晚宴 状压dp+质因数分解
挺神的一道题 ~ 由于两个人选的数字不能有互质的情况,所以说对于一个质因子来说,如果 1 选了,则 2 不能选任何整除该质因子的数. 然后,我们发现对于 1 ~ 500 的数字来说,只可能有一个大于 ...
- NOI 2015 寿司晚宴 (状压DP+分组背包)
题目大意:两个人从2~n中随意取几个数(不取也算作一种方案),被一个人取过的数不能被另一个人再取.两个人合法的取法是,其中一个人取的任何数必须与另一个人取的每一个数都互质,求所有合法的方案数 (数据范 ...
- 【BZOJ4197】[Noi2015]寿司晚宴 状压DP+分解质因数
[BZOJ4197][Noi2015]寿司晚宴 Description 为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴.小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴 ...
- bzoj4197 [Noi2015]寿司晚宴——状压DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4197 首先,两个人选的数都互质可以看作是一个人选了一个数,就相当于选了一个质因数集合,另一个 ...
- 【BZOJ-4197】寿司晚宴 状压DP
4197: [Noi2015]寿司晚宴 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 694 Solved: 440[Submit][Status] ...
- [NOI2015]寿司晚宴 --- 状压DP
[NOI2015]寿司晚宴 题目描述 为了庆祝NOI的成功开幕,主办方为大家准备了一场寿司晚宴. 小G和小W作为参加NOI的选手,也被邀请参加了寿司晚宴. 在晚宴上,主办方为大家提供了n−1种不同的寿 ...
- B4197 [Noi2015]寿司晚宴 状压dp
这个题一开始想到了唯一分解定理,然后状压.但是显然数组开不下,后来想到每个数(n<500)大于19的素因子只可能有一个,所以直接单独存就行了. 然后正常状压dp就很好搞了. 题干: Descri ...
- [NOI2015]寿司晚宴——状压dp
题目转化:将2~n的数分成两组,可以不选,使得这两组没有公共的质因子.求方案数. 选择了一个数,相当于选择了它的所有质因子. 30分: 发现,n<=30的时候,涉及到的质因子也就10个.2,3, ...
- BZOJ 4197 NOI 2015 寿司晚宴 状压DP
4197: [Noi2015]寿司晚宴 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 694 Solved: 440[Submit][Status] ...
随机推荐
- Problem 1002-2017 ACM/ICPC Asia Regional Shenyang Online
网络赛:2017 ACM/ICPC Asia Regional Shenyang Online 题目来源:cable cable cable Problem Description: Connecti ...
- 【学时总结】◆学时·VIII◆ 树形DP
◆学时·VIII◆ 树形DP DP像猴子一样爬上了树……QwQ ◇ 算法概述 基于树的模型,由于树上没有环,满足DP的无后效性,可以充分发挥其强大统计以及计算答案的能力. 一般来说树形DP的状态定义有 ...
- 转 MYSQL 命令行大全 (简洁、明了、全面)
MYSQL常用命令 .导出整个数据库 mysqldump -u 用户名 -p –default-character-set=latin1 数据库名 > 导出的文件名(数据库默认编码是latin1 ...
- 执行pip命令时遇到 Fatal error in launcher: Unable to create process using '"'
电脑同时安装了python-2.7.13跟python-3.6.1,安装时勾选了pip,环境变量也已经配置好. 为了方便运行,同时修改了可执行文件为 python2和python3.此时在cmd命令行 ...
- JAVAOOP接口
狭义接口:用来约束实现类中方法的长相的. 广义接口:已将编写好的功能. 1.接口中的方法都是抽象方法,没有方法体,必须被子类重写 2.java的接口变量都是静态常量 3.接口方法只不过是用来约束现实类 ...
- python-无参函数
#!/usr/local/bin/python3 # -*- coding:utf-8 -*- ''' #-----------定义函数---------- def func1(): "te ...
- MLT教程:从BXL文件导入Altium Designer原理图封装和PCB封装
在TI官网的封装文件中提供弄BXL文件可以导出Altium Designer的封装库和原理图库. 这个界面往下面拉会看到: 然后可以下载各种封装的bxl文件了.下面视频说明bxl文件如何导出成功. 如 ...
- 使用selenium模拟登录知乎
网上流传着许多抓取知乎数据的代码,抓取它的数据有一个问题一定绕不过去,那就是模拟登录,今天我们就来聊聊知乎的模拟登录. 获取知乎内容的方法有两种,一种是使用request,想办法携带cookies等必 ...
- C语言数组篇(五)多级指针和二维数组指针的区别
多级指针 以二级指针为例 二级指针的由来是 指针数组 的指针形式. int *p[10] 读取的顺序是 p[] --> 10个空间的数组 * p[] --> 这10个空间的数组里面存放 ...
- MySQL触发器和更新操作
一.触发器概念 触发器(trigger):监视某种情况,并触发某种操作,它是提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动 ...