CodeChef CSUBSQ Count Subsequences

题目大意

◇题目传送门◆

如果一个整数序列的各元素之和可以被给定整数 KKK 整数,则称这个序列是好的。给定整数序列 A1,A2,…,ANA_1, A_2, \ldots, A_NA1​,A2​,…,AN​。Hasan 想计算该序列有多少子序列是好的。不过这样的话问题就太简单了,因此 Hasan 决定再加一个限制。

任意非空下标序列 i1<i2<⋯<iLi_1< i_2 < \cdots < i_Li1​<i2​<⋯<iL​ 对应子序列 Ai1,Ai2,…,AiLA_{i_1}, A_{i_2}, \ldots , A_{i_L}Ai1​​,Ai2​​,…,AiL​​,如果满足 iL−i1≥Wi_L - i_1 \ge WiL​−i1​≥W 则称这个子序列非常好。请帮 Hasan 统计非常好的子序列方案数。由于答案可能非常大,请输出方案数对 109+710^9 + 7109+7 取模的结果。

分析

假设没有 WWW 这个限制,那么这个问题就变得简单了,就是一个简单的背包问题。

我们如果先固定左端点 LLL,那么我们需要的就是在区间 [L+W,N][L + W, N][L+W,N] 中查询一个元素,使它与左端点 LLL 之间选出任意多个元素,它们的和模 KKK 等于 000。

那么可以设计一个这样的状态:

状态 f(L,i,j)f(L, i, j)f(L,i,j) 表示当前区间左端点为 LLL (外层循环枚举),当前加入第 iii 个数字,和模 KKK 为 jjj 的方案数。

不难得出以下初始状态和转移:

初始状态:f(L,L,0)=1f(L, L, 0) = 1f(L,L,0)=1 和 ∀j∈[1,K−1],f(L,L,j)=0\forall j \in [1, K - 1],f(L, L, j) = 0∀j∈[1,K−1],f(L,L,j)=0。

转移:f(L,i,j)=f(L,i−1,j)+f(L,i−1,(j−Ai+K)modK)f(L, i, j) = f(L, i - 1, j) + f(L, i - 1, (j - A_i + K) \mod K)f(L,i,j)=f(L,i−1,j)+f(L,i−1,(j−Ai​+K)modK)。

显然最后的答案就是对于每个左端点 LLL,在区间 [L+1,N][L + 1, N][L+1,N] 中选择元素之和模 KKK 等于 000 的方案数 减掉 在区间 [L+1,L+W−1][L + 1, L + W - 1][L+1,L+W−1] 中选择元素之和模 KKK 等于 000 的方案数。

显然直接采用这种方法枚举左端点 LLL 计算就可以超时。

那么考虑采用分治的方法进行优化。

设当前我们正在处理的区间为 [L,R][L, R][L,R],我们将它拆分成两个子区间 [L,mid][L, mid][L,mid] 和 [mid+1,R][mid + 1, R][mid+1,R]。(midmidmid 是当前区间的中点),通过递归得到这两个子区间的答案。

那么我们就定义两个 DP 状态:fL(p,k),fR(p,k)f_L(p, k), f_R(p, k)fL​(p,k),fR​(p,k),分别表示在区间 [p,mid][p, mid][p,mid] 上有多少个序列的和模 KKK 等于 kkk,区间 [mid,p][mid, p][mid,p] 上有多少个子序列的和模 KKK 等于 kkk。

计算这两个 DP 状态的方法和上面介绍的一样。

那么考虑如何计算当前点的贡献。

我们记当前左边的元素之和模 KKK 等于 klk_lkl​,右边的元素之和模 KKK 等于 krk_rkr​。需要保证 kl+krk_l + k_rkl​+kr​ 模 KKK 等于 000。

那么我们就先枚举 [L,mid][L, mid][L,mid] 的一个左端点 lll,强制它要选(也就是它就是第一个)。那么如何计算这个如何计算,我们分成两种情况讨论:


情况1: 当我们所选取的 l+W−1≥midl + W - 1 \ge midl+W−1≥mid 时。

这时的答案就是必须选 lll,在区间 [l+1,mid][l + 1, mid][l+1,mid] 里任意选择的方案数乘上在区间 [mid+1,l+W−1][mid + 1, l + W - 1][mid+1,l+W−1] 随意选且在区间 [l+W,R][l + W, R][l+W,R] 选择至少一个的方案数。

用我们计算出的 DP 值表示就是:

(fL(l,kl)−fL(l+1,kl))×(fR(R,kr)−fR(l+W−1,kr))(f_L(l, k_l) - f_L(l + 1, k_l)) \times (f_R(R, k_r) - f_R(l + W - 1, k_r)) (fL​(l,kl​)−fL​(l+1,kl​))×(fR​(R,kr​)−fR​(l+W−1,kr​))

情况2: 当我们所选取的 l+W−1<midl + W - 1 < midl+W−1<mid 时。

我们发现无法直接计算出相应的答案,但我们可以将它拆成两部分:

  1. 必须选择 lll,且在 [mid+1,R][mid + 1, R][mid+1,R] 中选至少一个,在 [l+1,mid][l + 1, mid][l+1,mid] 中随便选的方案数:可以利用我们计算的 DP 表示为 (fL(l,kl)−fL(l+1,kl))×fR(R,kr)−fR(mid,kr)(f_L(l, k_l) - f_L(l + 1, k_l)) \times f_R(R, k_r) - f_R(mid, k_r)(fL​(l,kl​)−fL​(l+1,kl​))×fR​(R,kr​)−fR​(mid,kr​);
  2. 必须选择 lll,且在 [mid+1,R][mid + 1, R][mid+1,R] 中不选,在 [l+W,mid][l + W, mid][l+W,mid] 中选至少一个,在 [l+1,l+W−1][l + 1, l + W - 1][l+1,l+W−1] 中随便选的方案数,这个我们采用递归的方法进行处理。

总时间复杂度为 O(NKlog⁡2N)O(NK\log_2 N)O(NKlog2​N)。

参考代码

#include <cstdio>
#include <algorithm>
using namespace std;typedef long long ll;
const int Maxn = 1e5;
const int Maxk = 50;
const ll Mod = 1e9 + 7;int N, K, W;
int A[Maxn + 5];ll fLef[Maxn + 5][Maxk + 5], fRig[Maxn + 5][Maxk + 5];
ll ans;void Divide(int lb, int ub) {if(ub - lb < W) return;if(lb == ub) {if(A[lb] == 0) ans = (ans + 1) % Mod;return;}int mid = (lb + ub) >> 1;Divide(lb, mid), Divide(mid + 1, ub);for(int k = 0; k < K; k++)fRig[mid][k] = fLef[mid + 1][k] = 0;fRig[mid][0] = 1;for(int j = mid + 1; j <= ub; j++)for(int k = 0; k < K; k++)fRig[j][k] = (fRig[j - 1][k] +fRig[j - 1][(k - A[j] + K) % K]) % Mod;fLef[mid + 1][0] = 1;for(int j = mid; j >= lb; j--)for(int k = 0; k < K; k++)fLef[j][k] = (fLef[j + 1][k] +fLef[j + 1][(k - A[j] + K) % K]) % Mod;ll tot = 0;for(int lefsum = 0; lefsum < K; lefsum++) {int rigsum = (K - lefsum + K) % K;for(int j = lb; j + W <= ub && j <= mid; j++) {ll lef = (fLef[j][lefsum] - fLef[j + 1][lefsum] + Mod) % Mod, rig;if(j + W - 1 >= mid)rig = (fRig[ub][rigsum] - fRig[j + W - 1][rigsum] + Mod) % Mod;else rig = (fRig[ub][rigsum] - fRig[mid][rigsum] + Mod) % Mod;tot = (tot + lef * rig % Mod) % Mod;}}ans = (ans + tot) % Mod;
}int main() {#ifdef LOACLfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifint _;scanf("%d", &_);while(_--) {scanf("%d %d %d", &N, &K, &W);for(int i = 1; i <= N; i++) scanf("%d", &A[i]);ans = 0;Divide(1, N);printf("%lld\n", ans);}return 0;
}

【CodeChef】【DP】Count Subsequences相关推荐

  1. UVA10074 Take the Land【最大子段和+DP】

    The poor man went to the King and said, "Lord, I cannot maintain my family. Please give me some ...

  2. Bailian2766 最大子矩阵【最大子段和+DP】

    最大子矩阵 总时间限制: 1000ms 内存限制: 65536kB 描述 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵. 比如,如下4 ...

  3. 51Nod-1051 最大子矩阵和【最大子段和+DP】

    1051 最大子矩阵和 基准时间限制:2秒 空间限制:131072KB 分值:40难度:4级算法题 一个M*N的矩阵,找到此矩阵的一个子矩阵,并且这个子矩阵的元素的和是最大的,输出这个最大的值. 例如 ...

  4. P5405 [CTS2019]氪金手游 【数学概率+树形dp】

    P5405 [CTS2019]氪金手游 [数学概率+树形dp] 先考虑外向树的情况: 这个的关键是要把求满足拓扑序的概率转化为求 每个点都比它的子树中的所有节点先取到的概率 .单个节点 xxx 的概率 ...

  5. HDU-5542-The Battle of Chibi【树状数组+dp】

    HDU-5542-The Battle of Chibi[树状数组+dp] Time Limit: 6000/4000 MS (Java/Others) Memory Limit: 65535/655 ...

  6. NUC1157 To the Max【最大子段和+DP】

    To the Max 时间限制: 1000ms 内存限制: 65536KB 通过次数: 1总提交次数: 1 问题描述 Given a two-dimensional array of positive ...

  7. BZOJ2131 免费的馅饼【线段树优化DP】

    Input 第一行是用空格隔开的二个正整数,分别给出了舞台的宽度W(1到10^8之间)和馅饼的个数n(1到10^5). 接下来n行,每一行给出了一块馅饼的信息.由三个正整数组成,分别表示了每个馅饼落到 ...

  8. loj 300 [CTSC2017]吉夫特 【Lucas定理 + 子集dp】

    题目链接 loj300 题解 orz litble 膜完题解后,突然有一个简单的想法: 考虑到\(2\)是质数,考虑Lucas定理: \[{n \choose m} = \prod_{i = 1} { ...

  9. 【尺取或dp】codeforces C. An impassioned circulation of affection

    http://codeforces.com/contest/814/problem/C [题意] 给定一个长度为n的字符串s,一共有q个查询,每个查询给出一个数字m和一个字符ch,你的操作是可以改变字 ...

  10. 【单调队列优化dp】jzoj4883灵知的太阳信仰 纪中集训提高B组

    [NOIP2016提高A组集训第12场11.10]灵知的太阳信仰 (File IO): input:array.in output:array.out Time Limits: 1000 ms Mem ...

最新文章

  1. 关于NameError: name ‘sns‘ is not defined->ModuleNotFoundError: No module named ‘seaborn‘错误提示
  2. 常量指针与指向常量的指针
  3. 使用pip来安装pyOpenSSL
  4. dev控件中LookUpEdit的数据绑定问题
  5. Spring IOC 容器根据Bean 名称或者类型进行autowiring 自动依赖注入
  6. vue实现侧边折叠菜单栏手风琴效果
  7. i = i++ 之后 i 的值为什么是 0
  8. java给mongo数组添加_如何使用具有新值的java在mongodb中的现有集合中追加现有数组...
  9. 线程执行者(八)执行者周期性地运行一个任务
  10. spring cloud gateway 源码解析(4)跨域问题处理
  11. android 实现页面跳转代码,Android 实现页面跳转
  12. {工作记录}遇到过的网络攻击合集爬虫User-Agent记录..{持续更新}
  13. 【开源教程8】疯壳·开源编队无人机-GPIO(LED 航情灯、信号灯控制)
  14. Windows10 家庭版关闭安全中心(defender)
  15. 美团点评女技术总监任登君:不要给自己的人生设限
  16. Python到底是干啥的?
  17. Kali Linux 工具使用中文说明书
  18. 【Java 8 新特性】Java 8 Collectors示例
  19. 分布式锁实现业务幂等
  20. matlab 连续下标表示,MATLAB通过下标访问多个数组元素

热门文章

  1. 心魔,只有自己才能战胜。只要有心做,必定能达成。
  2. 阅读笔记 |《科学史和科学哲学导论》舒斯特
  3. java程序员建议考软考中级的哪一个证书
  4. 丘成桐科学奖计算机类,丘成桐科学奖
  5. python求f的倒数_Python中整数的倒数
  6. 国际足联正式裁决莫德斯特闹剧 此事或还有反转?
  7. 数据分析工具集合:Tableau入门及其他工具简介
  8. 高效操作字串的String Reference类
  9. STM32 之 MDA
  10. php八进制十六进制,php 实现进制转换(二进制、八进制、十六进制)互相转换实现代码...