一道玄学题...

其实一开始想的是对的,优化一下就好了

首先我们会发现,乘积不能被完全平方数整除等价于所有因子的每个质因子个数和都至多为1

可是500以内的质数很多,全找出来会爆炸的

可我们会发现,如果一个数的平方会在500以内,那么这个数一定<=22!

所以,1~500中会存在的完全平方数的质因子一定在22以内

这些质数只有八个,所以我们可以找出来

至于剩下的部分,显然23和46是不嫩同时出现的,所以我们把含有23这个因子的所有数分到一个背包里,对每个背包只允许使用其中的一个数,这样就能满足23等只出现一次,对其他更大的质数也是同理

什么?你说会不会重?

23*23>500,那么剩下所有的乘积都大于500,也就是说如果我们只用23及以上的质数分组,每组中的元素是互不重叠的。

什么?你说前面几个小的质数怎么办?

状压啊!

求出每个数对于前面八个质数(是的,小于23的质数有8个,分别为2,3,5,7,11,13,17,19),而言质因子的状态,用二进制表示(如15的质因子状态可以表示为110,即0个2,1个3,一个5)

至于那些用大质数表示不了的数,我们把他们每个单独分包即可。

在转移的时候,我们就可以第一层:枚举所有包,第二层:枚举包中每个元素,第三层:枚举前八个质数的所有状态,第四层:枚举前面用过的数的个数

这样就可以实现转移了,因为每次转移都是上一个包向下一个包去转移,所以包中元素是没有重复的。

当然,我本人在写代码的时候,选择把所有不能分到其他包里的元素全分到第一个包里,然后单独处理第一个包。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
#define mode 1000000007
using namespace std;
int cnt=;
int pri[];
bool vis[];
ll dp[][(<<)+][];
int sit[];
bool used[];
int bag[][];
int cct[];
int posi[];
int n,k;
void init()
{
for(int i=;i<=;i++)
{
if(!vis[i])
{
pri[cnt++]=i;
}
for(int j=;j<cnt&&i*pri[j]<=;j++)
{
vis[i*pri[j]]=;
if(!i%pri[j])
{
break;
}
}
}
}
void get_bag()
{
bag[][++cct[]]=;
for(int i=;i<=;i++)
{
bool flag=;
for(int t=;t<cnt&&i>=pri[t];t++)
{
if(i%pri[t]==)
{
cct[t-]++;
flag=;
bag[t-][cct[t-]]=i;
break;
}
}
if(!flag)
{
cct[]++;
bag[][cct[]]=i;
}
}
}
void get_ori()
{
sit[]=;
for(int i=;i<=;i++)
{
int t=i;
for(int j=;j<;j++)
{
int cot=;
while(t%pri[j]==)
{
t/=pri[j];
cot++;
}
if(cot>=)
{
used[i]=;
break;
}else if(cot==)
{
sit[i]|=(<<j);
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
init();
get_ori();
get_bag();
while(T--)
{
scanf("%d%d",&n,&k);
memset(dp,,sizeof(dp));
dp[][][]=;
int now=,past=;
for(int i=;i<=cct[];i++)
{
memset(dp[now],,sizeof(dp[now]));
for(int j=;j<(<<);j++)
{
for(int t=;t<=k;t++)
{
dp[now][j][t]=dp[past][j][t];
}
}
for(int j=;j<(<<);j++)
{
for(int t=;t<k;t++)
{
if(!(sit[bag[][i]]&j)&&!used[bag[][i]]&&bag[][i]<=n)
{
dp[now][j|sit[bag[][i]]][t+]+=dp[past][j][t];
dp[now][j|sit[bag[][i]]][t+]%=mode;
}
}
}
swap(now,past);
}
for(int i=;i<=cnt-;i++)
{
memset(dp[now],,sizeof(dp[now]));
for(int j=;j<(<<);j++)
{
for(int t=;t<=k;t++)
{
dp[now][j][t]=dp[past][j][t];
}
}
for(int f=;f<=cct[i];f++)
{
for(int j=;j<(<<);j++)
{
for(int t=;t<k;t++)
{
if(!(sit[bag[i][f]]&j)&&!used[bag[i][f]]&&bag[i][f]<=n)
{
dp[now][j|sit[bag[i][f]]][t+]+=dp[past][j][t];
dp[now][j|sit[bag[i][f]]][t+]%=mode;
}
}
}
}
swap(now,past);
}
ll ans=;
for(int i=;i<(<<);i++)
{
for(int j=;j<=k;j++)
{
ans+=dp[past][i][j];
ans%=mode;
}
}
printf("%lld\n",ans%mode);
}
return ;
}

hdu 6125 状压dp+分组的更多相关文章

  1. NOI 2015 寿司晚宴 (状压DP+分组背包)

    题目大意:两个人从2~n中随意取几个数(不取也算作一种方案),被一个人取过的数不能被另一个人再取.两个人合法的取法是,其中一个人取的任何数必须与另一个人取的每一个数都互质,求所有合法的方案数 (数据范 ...

  2. HDU 4778 状压DP

    一看就是状压,由于是类似博弈的游戏.游戏里的两人都是绝对聪明,那么先手的选择是能够确定最终局面的. 实际上是枚举最终局面情况,0代表是被Bob拿走的,1为Alice拿走的,当时Alice拿走且满足变换 ...

  3. NOIP模拟 乘积 - 状压dp + 分组背包

    题目大意: 给出n和k,求从小于等于n的数中取出不超过k个,其乘积是无平方因子数的方案数.无平方因子数:不能被质数的平方整除. 题目分析: 10(枚举\(n\le8\)),40(简单状压\(n\le1 ...

  4. HDU - 6125: Free from square (状压DP+分组背包)

    problem:给定N,K.表示你有数1到N,让你最多选择K个数,问有多少种方案,使得选择的数的乘积无平方因子数.N,K<500: solution:显然可以状压DP做,但是500以内的素数还是 ...

  5. HDU 3001 状压DP

    有道状压题用了搜索被队友骂还能不能好好训练了,, hdu 3001 经典的状压dp 大概题意..有n个城市 m个道路  成了一个有向图.n<=10: 然后这个人想去旅行.有个超人开始可以把他扔到 ...

  6. hdu 2809(状压dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2809 思路:简单的状压dp,看代码会更明白. #include<iostream> #in ...

  7. hdu 2167(状压dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2167 思路:经典的状压dp题,前后,上下,对角8个位置不能取,状态压缩枚举即可所有情况,递推关系是为d ...

  8. Engineer Assignment HDU - 6006 状压dp

    http://acm.split.hdu.edu.cn/showproblem.php?pid=6006 比赛的时候写了一个暴力,存暴力,过了,还46ms 那个暴力的思路是,预处理can[i][j]表 ...

  9. hdu 3254 (状压DP) Corn Fields

    poj 3254 n乘m的矩阵,1表示这块区域可以放牛,0,表示不能,而且不能在相邻的(包括上下相邻)两个区域放牛,问有多少种放牛的方法,全部不放也是一种方法. 对于每块可以放牛的区域,有放或者不放两 ...

随机推荐

  1. Python介绍以及安装

    Python介绍以及安装 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 借用我的导师的一句话:当你看到这篇文章的时候,那么恭喜你,你已经是踏入了开发的大门!欢迎加入:高级运维工程师 ...

  2. javascript 缓动返回顶部案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. Java编程思想 学习笔记3

    三.操作符 1.优先级 当一个表达式中存在多个操作符时,操作符的优先级就决定了各部分的计算顺序.程序员常常忘记其他优先级规则,所以应该用括号明确规定计算顺序. 当编译器观察到一个String后面紧跟着 ...

  4. 神级程序员通过两句话带你完全掌握Python最难知识点——元类!

    千万不要被所谓"元类是99%的python程序员不会用到的特性"这类的说辞吓住.因为 每个中国人,都是天生的元类使用者 学懂元类,你只需要知道两句话: 道生一,一生二,二生三,三生 ...

  5. SEO之robots.txt

    [关键词:robot.txt,sitemap,User-Agent,Disallow,Allow][声明:摘自Wikipedia] 1. 定义:robots.txt(统一小写)是一种存放于网站根目录下 ...

  6. 设计模式之UML类图六大关系辨析【2】

    六大关系:继承(extends).实现(Realization).依赖(use-a).关联(association).聚合(has-a).组合(强聚合)(Composition). 类与类之间的强弱关 ...

  7. JavaScript之字符串匹配工具[插件]

    /*** * StringCheckUtil * 字符串检测工具 * * @version 1.0 * @method isNum(string,scope) * @method isChinese( ...

  8. Ubuntu 18.04换国内源 中科大源 阿里源 163源 清华源

    感觉还是18.4好用,所以最近装回了18,感觉现在18的兼容性也还可以了,深度学习的环境配置都没有问题,就是安装软件的时候有点慢,所以想要更新一下源. 第一步: 编辑/etc/apt/sources. ...

  9. 创建Git独立分支

    在使用git进行版本控制的某些场景中我们可能需要在一个项目中建立完全独立的分支,此分支将作为一个独立的版本历史根节点,不与之前任何分支拥有相同的版本祖先. 比如当我们要在一个项目中使用一个分支进行项目 ...

  10. Android WebView常见问题及解决方案汇总【很全很实用】

    http://www.cnblogs.com/olartan/p/5713013.html