取数字(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} ...
随机推荐
- (转)C# -- 扩展方法的应用(Extension Methods)
本文转载自:http://blog.csdn.net/zxz414644665/article/details/9793205 当你有下面这样一个需求的时候,扩展方法就会起到作用:在项目中,类A需要添 ...
- windows下socket编程:区分shutdown()及closesocket()
以下描述主要是针对windows平台下的TCP socket而言. 首先需要区分一下关闭socket和关闭TCP连接的区别,关闭TCP连接是指TCP协议层的东西,就是两个TCP端之间交换了一些协议包( ...
- react核心知识点高度总结
本文系统的将react的语法以最简练的方式列举出来 安装 写在前面 JSX 组件的定义 state 生命周期 方法 条件渲染 列表 表单 组合嵌套 扩展语法 context传递props 错误拦截 r ...
- 初识Vue练习
<html lang="en"> <head> <meta charset="UTF-8"> <title>Ti ...
- 使用AJAX异步提交表单的几种方式
方式一 手工收集所有的用户输入,封装为大的“k1=v1&k2=v2…”键值对形式,使用$.post(url, data,fn)把数据提交给服务器 $.ajax({ type:'post', u ...
- 偏好设置(Preference)
一.Preference简介 (1)偏好设置是专门用来保存应用程序的配置信息的, 一般情况不要在偏好设置中保存其他数据.如果利用系统的偏好设置来存储数据, 默认就是存储在Library/Prefere ...
- java中常用的时间操作
最近项目设计时间的转换和计算,长时间没用时间操作了,感觉手有点生,所以在这里记录一下: Date 常用的方法: getTime() .setTime(): SimpleDateFormate 常用的方 ...
- TCP/IP 笔记 6 netstat -s 命令查看每个协议统计数据
netstat -s 命令,查看每个协议统计数据的常用方法 lenovo-myc@lenovomyc-Lenovo-Product:~$ netstat -s Ip: total packets re ...
- 一段PHP异常
这是我写的一段代码,里面通过PHP异常功能,实现报错时显示出错代码所在行.当使用者操作出错时,截图给我,我可以很快得去追踪和排查错误! public function added_business_s ...
- group()、start()、end()、span()