取数字(dp优化)
取数字(dp优化)
给定n个整数\(a_i\),你需要从中选取若干个数,使得它们的和是m的倍数。问有多少种方案。有多个询问,每次询问一个的m对应的答案。
\(1\le n\le 200000,1\le m\le 100,1\le q\le 30,-10^9\le a_i\le 10^9\)。
首先有一个暴力dp:\(f[i][j]\)表示选到第i个数,和mod m是j的方案数。但是显然,这个dp是\(O(nm)\)的,然后就tle了。
换一个思路dp?由于我们只需要一个数模m的值,可以先把所有数模m放到桶里,用\(f[i][j]\)表示选到第i个桶,余数和为j的方案数。那么,枚举这一个桶放了多少数k(rh奇怪的dp方法),\(f[i][j+ki]+=f[i-1][j]*\binom{b[i]}{k}\)。
这个dp是\(O(nm^2)\)的,能过50%。先把代码放上来:
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const LL maxn=4e5+5, maxm=105, mod=1e9+7;
LL n, q, m, a[maxn], b[maxm], f[maxm][maxm];
LL fac[maxn], inv[maxn];
LL C(LL x, LL y){ return fac[x]*inv[y]%mod*inv[x-y]%mod; }
LL fpow(LL a, LL x){
LL ans=1, base=a;
for (; x; x>>=1, base*=base, base%=mod)
if (x&1) ans*=base, ans%=mod;
return ans;
}
int main(){
scanf("%lld%lld", &n, &q);
fac[0]=1; inv[0]=1;
for (LL i=1; i<maxn; ++i) fac[i]=(i*fac[i-1])%mod, inv[i]=fpow(fac[i], mod-2);
for (LL i=1; i<=n; ++i) scanf("%lld", &a[i]);
while (q--){
memset(b, 0, sizeof(b));
memset(f, 0, sizeof(f));
scanf("%lld", &m);
for (LL i=1; i<=n; ++i) ++b[(a[i]%m+m)%m];
f[0][0]=fpow(2, b[0]);
for (LL i=1; i<m; ++i){ //第几个桶
for (LL w=0; w<m; ++w) //所有数的和的余数是w
for (LL j=0; j<=b[i]; ++j) //这个桶取了多少数
(f[i][(j*i+w)%m]+=f[i-1][w]*C(b[i], j))%=mod;
}
printf("%lld\n", f[m-1][0]);
}
return 0;
}
我们在\((f[i][(j*i+w)\%m]+=f[i-1][w]*C(b[i], j))\%=mod;\)这行里可以发现:虽然j枚举到了b[i],但是\((j*i+w)\)模了m。因此,只要枚举\(j=[0,m-1]\)就行了!
所以说:
- dp要多考虑几种方法突破。
- dp方程里如果带模数,有时是可以优化的。
- 只要不把求状态的顺序颠倒,可以交换dp中两个循环的位置
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL maxn=4e5+5, maxm=105, mod=1e9+7;
LL n, q, m, a[maxn], b[maxm], f[maxm][maxm];
LL fac[maxn], inv[maxn], pre[maxn];
inline int min(LL x, LL y){ return x<y?x:y; }
LL C(LL x, LL y){ return fac[x]*inv[y]%mod*inv[x-y]%mod; }
LL fpow(LL a, LL x){
LL ans=1, base=a;
for (; x; x>>=1, base*=base, base%=mod)
if (x&1) ans*=base, ans%=mod;
return ans;
}
int getint(){
char c; int flag=1, re=0;
for (c=getchar(); !isdigit(c); c=getchar())
if (c=='-') flag=-1;
for (re=c-48; c=getchar(), isdigit(c); re=re*10+c-48);
return re*flag;
}
int main(){
scanf("%lld%lld", &n, &q);
fac[0]=1; inv[0]=1;
for (LL i=1; i<maxn; ++i) fac[i]=(i*fac[i-1])%mod, inv[i]=fpow(fac[i], mod-2);
for (LL i=1; i<=n; ++i) scanf("%lld", &a[i]);
while (q--){
memset(b, 0, sizeof(b));
memset(f, 0, sizeof(f));
m=getint();
for (LL i=1; i<=n; ++i) ++b[(a[i]%m+m)%m];
f[0][0]=fpow(2, b[0]);
for (LL i=1; i<m; ++i){ //第几个桶
for (LL j=0; j<=b[i]; ++j){
if (j<m) pre[j%m]=0;
(pre[j%m]+=C(b[i], j))%=mod;
}
for (LL w=0; w<m; ++w) //所有数的和的余数是w
for (LL j=0; j<=min(m-1, b[i]); ++j) //这个桶取了多少数
(f[i][(j*i+w)%m]+=f[i-1][w]*pre[j])%=mod;
}
printf("%lld\n", f[m-1][0]);
}
return 0;
}
取数字(dp优化)的更多相关文章
- 连续取数字DP使值最大HDU2697
题意: 有n个数,每个数都有价钱,连续的取可以获得len*len的利益,使利益最大. 思路: 三维DP,1.2.3维分别是第i个,剩余多少钱,从后往前连续的有几个. #define IOS ios_b ...
- [总结]一些 DP 优化方法
目录 注意本文未完结 写在前面 矩阵快速幂优化 前缀和优化 two-pointer 优化 决策单调性对一类 1D/1D DP 的优化 \(w(i,j)\) 只含 \(i\) 和 \(j\) 的项--单 ...
- Slope Trick:解决一类凸代价函数DP优化
[前言] 在补Codeforce的DP时遇到一个比较新颖的题,然后在知乎上刚好 hycc 桑也写了这道题的相关题解,这里是作为学习并引用博客的部分内容 这道题追根溯源发现2016年这个算法已经在API ...
- DP 优化方法大杂烩 & 做题记录 I.
标 * 的是推荐阅读的部分 / 做的题目. 1. 动态 DP(DDP)算法简介 动态动态规划. 以 P4719 为例讲一讲 ddp: 1.1. 树剖解法 如果没有修改操作,那么可以设计出 DP 方案 ...
- 蓝桥杯历届试题 地宫取宝 dp or 记忆化搜索
问题描述 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角,出口在右下角. 小明被带到地宫的入口,国王要求他只能向右或向下行走. 走 ...
- NOIP2015 子串 (DP+优化)
子串 (substring.cpp/c/pas) [问题描述] 有两个仅包含小写英文字母的字符串 A 和 B.现在要从字符串 A 中取出 k 个 互不重 叠 的非空子串,然后把这 k 个子串按照其在字 ...
- 【学习笔记】动态规划—各种 DP 优化
[学习笔记]动态规划-各种 DP 优化 [大前言] 个人认为贪心,\(dp\) 是最难的,每次遇到题完全不知道该怎么办,看了题解后又瞬间恍然大悟(TAT).这篇文章也是花了我差不多一个月时间才全部完成 ...
- Codevs 1305 Freda的道路(矩阵乘法 DP优化)
1305 Freda的道路 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description Freda要到Rainbow的城堡去玩了.我们可以认 ...
- DP 优化小技巧
收录一些比较冷门的 DP 优化方法. 1. 树上依赖性背包 树上依赖性背包形如在树上选出若干个物品做背包问题,满足这些物品连通.由于 01 背包,多重背包和完全背包均可以在 \(\mathcal{O} ...
随机推荐
- 机器学习:集成学习(Bagging、Pasting)
一.集成学习算法的问题 可参考:模型集成(Enxemble) 博主:独孤呆博 思路:集成多个算法,让不同的算法对同一组数据进行分析,得到结果,最终投票决定各个算法公认的最好的结果: 弊端:虽然有很多机 ...
- 创建,查看,删除pool,查看,修改pool参数命令总结
标签(空格分隔): ceph,ceph运维,pool 1. 创建pool命令: ceph的pool有两种类型,一种是副本池,一种是ec池,创建时也有所区别 1.1 创建副本池: $ sudo ceph ...
- oracle sql 函数
(7)查询日期之间的数据 例如查询student表中出生日期(birthday)在’2016-01-01’ 和’2017-01-01’之间的数据: select * from student wher ...
- SQLServer SDK
https://www.cnblogs.com/Leo_wl/p/3451865.html 回到目录 SQLSERVER一些公用DLL的作用解释 如果你的SQLSERVER安装在C盘的话,下面的路径就 ...
- 部署和调优 1.1 nfs部署和优化-1
NFS服务会经常用到,用于在网络上共享存储.举一个例子来说明一下 NFS .假如有三台机器 A.B.C,它们需要访问同一个目录,目录中都是图片,传统的做法是把这些图片分别放到 A.B.C.但是,若使用 ...
- EF CODEFIRST WITH ORACLE 存储过程
EF CODEFIRST WITH ORACLE 解决存储过程一直没找到解决方案 所以最后也没办法还是用了最基本的解决方案 采用Oracle.ManagedDataAccess提供的ADO基础访问类 ...
- Java构造函数中调用构造函数
在Java中,当为一个类创建了多个构造函数时,有时想在一个构造函数中调用另一个构造函数以减少代码量.这时可以使用this关键字来实现. 通常,当使用this关键字时,它意味着"这个对象&qu ...
- 【总结整理】天地图WMTS服务与卫星图匹配与坐标转换
http://blog.sina.com.cn/s/blog_60abbd570102wnhh.html
- CSS代码重构与优化之路(转)
CSS代码重构与优化之路 阅读目录 CSS代码重构的目的 CSS代码重构的基本方法 CSS方法论 我自己总结的方法 写CSS的同学们往往会体会到,随着项目规模的增加,项目中的CSS代码也会越来越多 ...
- Etyma01 ced ceed cess
一. etyma ['ɛtə,mə] ced.ceed.cess -> go -> 行走,前进 二.for instance 1. precede=pre+ced+e pre- 在前 2. ...