取数字(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} ...
随机推荐
- [转载]centos下yum安装samba及配置
centos下yum安装samba及配置 在我们使用 Windows 作为客户机的时候,通常有文件.打印共享的需求.作为Windows 网络功能之一,通常可以在 Windows 客户机之间通过Wind ...
- live555源码分析----RSTPServer创建过程分析
最近五一回家,终于有机会能安静的看一下流媒体这方面相关的知识,准备分析live555的源码,接下来会把我读源码的过程记录成博客,以供其他的同路人参考. 因为再读源码的过程中,并不是一路顺着读下来,往往 ...
- HTML5实用知识点
本文讲解HTML5实用知识点 新增的表单type Canvas使用 SVG使用 Audio使用 Video使用 网页缓存 文件缓存 后台worker Server-Sent Events 定位 拖放功 ...
- PowerDesigner中批量替换name和code的脚本
无论是cdm还是pdm都可以批量替换.处理.可在Tool-Execute commands-Edit/Run script中编辑运行脚本: 下面的脚本是批量将CDM中实体的用Code替换掉Name O ...
- Oracle Management Packs
http://kerryosborne.oracle-guy.com/2008/10/oracle-management-packs/ There has been quite a bit of co ...
- hadoop 更改 tmp目录
配置hadoop临时目录--------------------- 1.配置[core-site.xml]文件 <configuration> <property> <n ...
- oracle常用函数总结(一)
最近在读数据库存储过程,或者在xml里写sql时用到数据库函数,笔者觉得有必要总结一下,当然有的函数笔者也很懵逼,不过可以问度娘啊!好了!开始正题. )s from dual;--1 若nvl第一个参 ...
- Eclipse中,将jar包导入为User Library
项目右键 Properties -> bulid path -> Add Library -> User Library -> User Libraries -> New ...
- c++对象模型探索(一)
粗略阅读了<深度探索c++对象模型>一书后,对c++对象底层的内存布局有了一些了解,但同时,也产生了一些疑惑: 1.将子类指针用dynamic_cast转成父类指针之后,其虚表指针会相应变 ...
- 使用网络用户命令行工具的/passwordreq:yes
提示:"新建域时,本地administrator帐户将成为域administrator账户.无法新建域,因为本地administrator账户密码不符合要求.目前,本地administrat ...