51nod 1354:选数字

51nod 1354:选数字

题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1354

题目大意:有$T(T \leqslant 20)$组数据,每组给出$n(n \leqslant 1000)$个数和$K(K \leqslant 100,000,000)$,问在这$n$个数中选取若干个,积为$K$的方案数有多少.

DP+离散化

与01背包类似,定义状态$dp[i][j]$为前$i$个数中选取若干个数,积为$j$的方案数.

但积过大($K \leqslant 100,000,000$),考虑到实际有用状态并不多,有用状态为$dp[i][d]$,其中$d$为$K$的因数.

故可以预处理出$K$的因数,将其离散化,从而把第二维降到$D(K)$,$D(K)$表示$K$的因子数.

算法时间复杂度为$O(n \times D(K) \times lg(D(K)))$,$lg$为离散化所带来的时间.

代码如下:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #define N 1005
 6 using namespace std;
 7 typedef long long ll;
 8 const ll M=1000000007;
 9 int T,n,K,a[N],k,fact[N],dp[N][N],t,ans;
10 int main(void){
11     scanf("%d",&T);
12     while(T--){
13         memset(dp,0,sizeof(dp));
14         scanf("%d%d",&n,&K);
15         for(t=1,k=0;t*t<K;++t)if(K%t==0){
16             fact[k++]=t;
17             fact[k++]=K/t;
18         }if(K==t*t)fact[k++]=t;
19         sort(fact,fact+k);
20         for(int i=1;i<=n;++i){
21             scanf("%d",&a[i]);
22             for(int j=0;j<k;++j)if(dp[i-1][j]){
23                 dp[i][j]=(dp[i][j]+dp[i-1][j])%M;
24                 if((ll)K%((ll)fact[j]*a[i]))continue;
25                 t=lower_bound(fact,fact+k,fact[j]*a[i])-fact;
26                 dp[i][t]=(dp[i][t]+dp[i-1][j])%M;
27             }
28             if(K%a[i])continue;
29             t=lower_bound(fact,fact+k,a[i])-fact;
30             dp[i][t]=(dp[i][t]+1)%M;
31         }
32         printf("%d\n",dp[n][k-1]);
33     }
34 }
时间: 2024-10-29 19:10:45

51nod 1354:选数字的相关文章

51nod1354 选数字

01背包tle. 解题报告(by System Message) 类似于背包的DP,以乘积为状态.先把等选数字里面不是K约数的去掉.然后找出K的约数,进行离散化.然后dp[i][j]表示前i个数字乘积为j的状态.Dp[i+1][j*a[i+1]]]+=dp[i][j].Dp[i+1][j]+=dp[i][j];总的复杂度是O(n*d(k)*log(d(k)))D(k)表示k的因子数目.多一个log是因为离散化了,对应下标的时候要二分查找. 其实我觉得就是去掉没用的状态只用他的约数来更新.网上有的

51nod 1770 数数字

1770 数数字 基准时间限制:1 秒 空间限制:262144 KB 分值: 20 难度:3级算法题  收藏  关注 统计一下 aaa ? aaan个a × b 的结果里面有多少个数字d,a,b,d均为一位数. 样例解释: 3333333333*3=9999999999,里面有10个9. Input 多组测试数据. 第一行有一个整数T,表示测试数据的数目.(1≤T≤5000) 接下来有T行,每一行表示一组测试数据,有4个整数a,b,d,n. (1≤a,b≤9,0≤d≤

【题解】选数字 [51nod1354]

[题解]选数字 [51nod1354] 传送门:选数字 \([51nod1354]\) [题目描述] 共 \(T\) 组测试点,每一组给定一个长度为 \(n\) 的序列和一个整数 \(K\),找出有多少子序列满足子序列中所有元素乘积恰好等于K,答案对 \(1e9+7\) 取模. [样例] 样例输入: 2 3 3 1 1 3 3 6 2 3 6 样例输出: 4 2 [数据范围] \(100\%\) \(1 \leqslant T \leqslant 20,\) \(1 \leqslant N \l

ACM学习历程—51NOD 1770数数字(循环节)

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1770 这是这次BSG白山极客挑战赛的A题.由于数字全部相同,乘上b必然会有循环节,于是模拟乘法,记录数据,出现循环就退出即可. 代码: #include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring>

51Nod - 1385 凑数字

51Nod - 1385 如果是n位数,x1 x2 x3 ... xn 从1到n的所有数中位数n-1的数字一定含有 1111...11,2222...22,...,9999...99 对于0 考虑n位数1000...00 其中有n-1个0 那么n-1位数中0-9都应该有n-1个(ans += 10*(n-1)) 考虑第一位数x1需要1, 2, 3, ..., x1(ans += x1) 但是如果第一位数大于第二位数 比如21 这时2最多出现一次,以为最高位出现时, 它的下一位不能出现2, 但是如

51nod 1132 覆盖数字的数量 V2

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1132 题意是给定a,b,l,r求[l,r]内有几个整数可以表示成ax+by(x,y为非负整数). 直接算l<=ax+by<=r会重复计算一个数的多种表示方法,而两种表示方法(x,y)总是相差k*(b,-a),因此限制y取最小值进行去重 即x>=0,y>=0,l<=ax+by<=r,y<a/gcd(a,b)五个半平面的交的整点个数,可以分类

51nod 1590 合并数字

1590 合并数字 STL - List 练习题, 直接用 List 模拟题意即可,或者手写链表也行. #include <bits/stdc++.h> using namespace std; const int N = 1e4 + 10; list<int> List[N]; int main(){ int n,x,y,y_idx,x_idx; cin >> n ; for(int i = 1;i <= n; ++i){//初始化全为 i List[i].pu

猜数字游戏的提示 (Master-Mind Hints, UVa 340)

题目: 实现一个经典"猜数字"游戏. 给定答案序列和用户猜的序列,统计有多少数字位置正确(A),有多少数字在两个序列都出现过但位置不对(B). 输入包含多组数据. 每组输入第一行为序列长度n,第二行是答案序列,接下来是若干猜测序列. 猜测序列全0时该组数据结束. n=0时输入结束. 样例输入:4 13 5 51 1 2 34 3 3 56 5 5 16 1 3 51 3 5 50 0 0 0101 2 2 2 4 5 6 6 6 91 2 3 4 5 6 7 8 9 11 1 2 2

由数字三角问题来理解DP

先看几类数字三角形的问题,通过对这几个问题的分析来理解有关动态规划的基本思想 数字三角形I 问题描述: 有一个由正整数组成的三角形,第一行只有一个数,除了最下行之外 每个数的左下方和右下方各有一个数,从第一行的数开始,每次可以往左下或右下走一格,直到走到三角形底端,把沿途经过的数全部加起来作为得分.如何走,使得这个得分尽量大? 分析: 如何走,是一个决策问题,很容易联想到一种贪心策略是:每次选数字大的那个方向走.然而很明显,这种决策方案是错误的.因为如果按这种方案,得到的结果是1→3→10→3,