乘积

题目背景

\(\mathrm{Smart}\) 最近在潜心研究数学, 他发现了一类很有趣的数字, 叫做无平方因子数。 也就是这一类数字不能够被任意一个质数的平方整除, 比如\(6\)、\(7\)、\(10\)都是无平方因子数, 而\(12\)则不是。

题目描述

所以 \(\mathrm{Smart}\) 在思考一个问题——选择不超过 \(K\) 个 \(N\) 以内的正整数乘起来, 使得乘积是一个无平方因子数, 有多少种取法? (每个数只能取一次)

输入输出格式

输入格式

第一行一个整数 \(T\) 表示数据组数。

接下来 \(T\) 行, 每行两个整数 \(N\),\(K\), 意思如题面所述。

输出格式

对于每一组数据, 输出一个整数表示取法的方案数对 \(10^9+7\) 取模后的数值。

说明

\(10\%\)的数据: \(N≤8\);

\(40\%\)的数据: \(N≤16\);

\(70\%\)的数据: \(N≤30\);

\(100\%\)的数据: \(1≤T≤5\); \(1≤K≤N≤500\)。


70pts 有非常多种搞法,然而状压是最难写的但是最可能继续玩出正解的。。

可是我太菜,比赛时写了个麻烦的状压

\(dp[i][j][s]\)代表前\(i\)个数选择了\(j\)个素数状态为\(s\)的方案数

Code:

#include <cstdio>
#include <cstring>
const int N=502;
int num[30][10],dat[30];
int pri[N],is[N],v[N],cnt,tot;
int div[N][100];
void init()
{
for(int i=2;i<=500;i++)
{
if(!is[i])
{
pri[++cnt]=i;
v[i]=i;
}
for(int j=1;j<=cnt&&i*pri[j]<=500;j++)
{
if(v[i]<pri[j]) break;
v[i*pri[j]]=pri[j];
is[i*pri[j]]=1;
}
}
for(int i=2;i<=500;i++)
{
int t=i;
for(int j=1;j<=cnt;j++)
while(t%pri[j]==0)
div[i][j]++,t/=pri[j];
}
for(int i=2;i<=33;i++)
{
int flag=1;
for(int j=1;j<=12;j++)
if(div[i][j]>1) {flag=0;break;}
if(!flag) continue;
++tot;
dat[tot]=i;
for(int j=1;j<=12;j++)
if(div[i][j])
num[tot][j]=1;
}
}
int mod=1e9+7;
int n0,k;
int dp[20][20][1200];
void work()
{
scanf("%d%d",&n0,&k);
int l=0,n=0;
for(int i=1;;i++)//素数长度上界
{
if(pri[i]<=n0) ++l;
else break;
}
for(int i=1;;i++)//选数个数上界
{
if(dat[i]<=n0) ++n;
else break;
}
k=(k<=n?k:n);
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++) dp[i][0][0]=1;
int ans=0;
for(int i=1;i<=n;i++)//前i个数
for(int j=1;j<=k;j++)//取了j个
for(int s=1;s<1<<l;s++)//素数集合状态
{
int las=s,flag=1;
dp[i][j][s]=dp[i-1][j][s];
for(int q=1;q<=l;q++)
if(num[i][q])//如果这一位是1
{
if((s>>q-1)&1) las^=1<<q-1;//变成0
else {flag=0;break;}
}
if(flag) (dp[i][j][s]+=dp[i-1][j-1][las])%=mod;
}
for(int i=1;i<=k;i++)
for(int s=1;s<1<<l;s++)
{
if(i!=k) (ans+=dp[n][i][s]<<1)%=mod;
else (ans+=dp[n][i][s])%=mod;
}
printf("%d\n",ans+1);
}
int main()
{
freopen("mult.in","r",stdin);
freopen("mult.out","w",stdout);
init();
int t;
scanf("%d",&t);
while(t--)
work();
return 0;
}

而正解只是运用分组背包的思想

注意到大于19的质数只可能出现一个

那我们实际上就只需要找到状压2,3,5,7,11,13,17,19这几个素数就行了

其他大于19的素数按照这个素数进行分组,剩下的同时按前几个素数做就可以了

很巧妙的思想,然而数据不好造,答案一样的期望炒鸡高

Code:

#include <cstdio>
#include <cstring>
#include <vector>
#define ll long long
const int N=500;
const ll mod=1e9+7;
const int pri[9]={0,2,3,5,7,11,13,17,19};
using namespace std;
vector <int > g[N+10];
int belong[N+10],sta[N+10],n,k;
void init()
{
memset(belong,0,sizeof(belong));
memset(sta,0,sizeof(sta));
for(int i=1;i<=N;i++) g[i].clear();
for(int i=1;i<=n;i++)
{
belong[i]=i;
for(int j=1;j<=8;j++)
{
if(i%(pri[j]*pri[j])==0) {sta[i]=-1;break;}
else if(i%pri[j]==0) {belong[i]/=pri[j];sta[i]|=1<<j-1;}
}
}
for(int i=1;i<=n;i++)
{
if(~sta[i])
{
if(belong[i]!=1)
g[belong[i]].push_back(sta[i]);
else
g[i].push_back(sta[i]);
}
}
}
ll dp[N+10][256];
void work()
{
scanf("%d%d",&n,&k);
init();
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=k;j;j--)
for(int s=255;~s;s--)
{
if(!dp[j][s]) continue;
for(int l=0;l<g[i].size();l++)
{
int now=g[i][l];
if(now&s) continue;
(dp[j][now|s]+=dp[j-1][s])%=mod;
}
}
}
ll ans=0;
for(int i=1;i<=k;i++)
for(int s=0;s<=255;s++)
(ans+=dp[i][s])%=mod;
printf("%lld\n",ans);
}
int main()
{
//freopen("mult.in","r",stdin);
//freopen("mult.out","w",stdout);
int t;scanf("%d",&t);
while(t--)
work();
return 0;
}

2018.8.20

安徽师大附中%你赛day7 T2 乘积 解题报告的更多相关文章

  1. 安徽师大附中%你赛day9 T2 富 解题报告

    富 题目背景 出于某些原因, 苟先生在追杀富先生. 题目描述 富先生所在的地方是一个\(n\times m\)的网格,苟先生排出了他的狼狗大军,共有\(k\)条狗,第\(i\)条狗所在的位置为\((x ...

  2. 安徽师大附中%你赛day9 T3 贵 解题报告

    贵 问题描述 苟先生的狼狗大军没有追上富先生, 所以他把它们都解雇了, 决定去雇佣一些更好的狗, 不过狗可是很贵的.苟先生有 \(w\) 元钱, 有 \(n\) 条狗可以雇佣, 第 \(i\) 条狗有 ...

  3. 安徽师大附中%你赛day3T1 怜香惜玉 解题报告

    怜香惜玉 题意: 已知 \(f(x)=\frac{2 \times \sum_{(i,x)=1}^x i}{φ(x)}\) 先给定数据组数\(t\)和\(k\) 每组数据给出\(n\),求\(\sum ...

  4. 安徽师大附中%你赛day2T3 巧克力 解题报告

    巧克力 题目描述 小\(T\)有\(N\)块巧克力, 每块巧克力上都有一句话(由小写英文字母组成,不含标点) .现在每块巧克力都断成了若干截,更糟糕的是,有一些碎片丢失了 ,但是剩下的碎片之间的顺序是 ...

  5. 安徽师大附中%你赛day6 T3 Hamsters [POI2010]CHO-Hamsters 解题报告

    [POI2010]CHO-Hamsters 题意: 给出n个互不包含的字符串,要求你求出一个最短的字符串S,使得这n个字符串在S中总共至少出现m次,问S最短是多少? 范围: \(1 \le n \le ...

  6. 安徽师大附中%你赛day5 T3 树上行走 解题报告

    树上行走 题目背景 \(\mathrm{Smart}\) 的脑洞非常大, 经常幻想出一些奇怪的东西. 题目描述 某一天,\(\mathrm{Smart}\) 幻想出了一棵没有边际的二叉树,脑补着在那棵 ...

  7. 安徽师大附中%你赛day4T2 演讲解题报告

    演讲 题目背景: 众所周知,\(\mathrm{Zdrcl}\)是一名天天\(\mathrm{AK}\)的高水平选手. 作为一民长者,为了向大家讲述自己\(\mathrm{AK}\)的经验,他决定在一 ...

  8. 安徽师大附中%你赛day4T1 金字塔 解题报告

    金字塔 题目背景: \(Zdrcl\)带着妹子们来到了胡夫金字塔周边旅游, 发现这里正在进行一个有关金字塔的游戏 题目描述: 游戏规则如下: 1. 这里的金字塔是一个 \(N\) 阶的二维金字塔. 2 ...

  9. 「2018-12-02模拟赛」T2 种树 解题报告

    2.种树(tree.pas/cpp/in/out) 问题描述: Fanvree 很聪明,解决难题时他总会把问题简单化. 例如,他就整天喜欢把图转化为树.但是他不会缩环,那他怎么转化呢? 这是一个有 n ...

随机推荐

  1. 堆数据结构(heapq)简单应用

    ## 堆数据结构(heapq)简单应用 # 堆数据结构 heapq # 常用方法:nlargest(),nsmallest(),heapify(),heappop() # 如果需要的个数较小,使用nl ...

  2. hive自定义函数(UDF)

    首先什么是UDF,UDF的全称为user-defined function,用户定义函数,为什么有它的存在呢?有的时候 你要写的查询无法轻松地使用Hive提供的内置函数来表示,通过写UDF,Hive就 ...

  3. Python栈的学习资料

    持续更新... 1. 基础 Python for Everybody的视频课程,称得上深入浅出 https://www.py4e.com/ 2. 进阶 偏重实践应用,快速上手,稀饭~ https:// ...

  4. python学习——函数

     一.在python的世界里什么是函数: 答:函数通常是用来实现某一个功能二被封装成的一个对象,是用来实现代码复用的常用方式 现在有一个需求,假如你在不知道len()方法的情况下,要你计算字符串‘he ...

  5. Java学习笔记十四:如何定义Java中的类以及使用对象的属性

    如何定义Java中的类以及使用对象的属性 一:类的重要性: 所有Java程序都以类class为组织单元: 二:什么是类: 类是模子,确定对象将会拥有的特征(属性)和行为(方法): 三:类的组成: 属性 ...

  6. Makefile中wildcard的介绍

    在Makefile规则中,通配符会被自动展开.但在变量的定义和函数引用时,通配符将失效.这种情况下如果需要通配符有效,就需要使用函数“wildcard”,它的用法是:$(wildcard PATTER ...

  7. Unicode控制字符

    Unicode控制字符就是特殊的Unicode字符 控制字符转义代码对照表 Unicode-控制字符 ‎LRM ‏RLM ‍ZWJ ‌ZWNJ ‪LRE ‭LRO ‮RLO ‬PDF NADS  ...

  8. shell重温---基础篇(流程控制&if判断&for&while&循环操作)

        和Java.PHP等语言不一样,sh的流程控制不可为空,如(以下为PHP流程控制写法): <?php if (isset($_GET["q"])) { search( ...

  9. MongoDB入门---简介

    最近呢,刚好有一些时间,所以就学习了一下新的数据库类型MongoDB.要想了解这个MongoDB,我们首先需要了解一个概念,那就是nosql(not only sql).一下就是官方的概念: NoSQ ...

  10. hihoCoder 1175:拓扑排序二

    题目链接: http://hihocoder.com/problemset/problem/1175 题目难度:一星级(简单题) 今天闲来无事,决定刷一道水题.结果发现这道水题居然把我卡了将近一个钟头 ...