题面:

传送门

题目描述:

题意很简单:1.数组中的所有整数都在区间[l, r],2.所有元素之和能被3整除。现在知道这个数组的大小,l和r,问:按照题目的要求组成的数组一共有多少种可能。
 

题目分析:

这道题应该是一道常见的用dp来解决计数的问题。
1.如果忽略所有元素之和能被3整除,那么,按照正常的想法答案肯定是:(r-l+1)n。但是题目多了所有元素之和能被3整除,看似难度大了很多。其实,我们可以分析一下:忽略所有元素之和能被3整除,答案是怎样想出来的:每一个“位”上都有(r-l+1)种选择,然后把每个“位”上的选择乘起来,就是(r-l+1)n。因此,我们可以通过研究每个"位"上的选择来解决这个问题。但在实际情况中,这个“位”的选择实在是不好解决:要确定所有元素之和要被3整除,有些“位”可能是之前不能被3整除,经过几次累加后就能被3整除了,情况过于复杂。这时,我们要想一种能“简化过程”的思想:
把问题拆解子问题,对应的解决办法其中就有dp。我们可以这样定义问题:前n个数的和能被3模余p(p = 0, 1, 2)的可能有多少?那么其中一个子问题是:前n-1个数的和能被3模余p(p = 0, 1, 2)的可能有多少?换成更直观的图,其实就是:
前n-1位已经确定好(计算出前n-1位的可能数),如果确定了第n位的选择,那么根据组合数学计数原理,就可以用前n-1位计算出的结果计算出前n位一共有多少种选择。第n位的选择要看当前的“状态”是什么:也就是和p有关。
 
2.经过分析,有如下选择(这里用dp[p][n]来代表上面的含义):
当p == 0时:
dp[0][n] += dp[0][n-1] * (第n位选被3模后余数为0的数的个数)
dp[0][n] += dp[1][n-1] * (第n位选被3模后余数为2的数的个数)
dp[0][n] += dp[2][n-1] * (第n位选被3模后余数为1的数的个数)
 
当p == 1时:
dp[0][n] += dp[0][n-1] * (第n位选被3模后余数为1的数的个数)
dp[0][n] += dp[1][n-1] * (第n位选被3模后余数为0的数的个数)
dp[0][n] += dp[2][n-1] * (第n位选被3模后余数为2的数的个数)
 
当p == 2时:
dp[0][n] += dp[0][n-1] * (第n位选被3模后余数为2的数的个数)
dp[0][n] += dp[1][n-1] * (第n位选被3模后余数为1的数的个数)
dp[0][n] += dp[2][n-1] * (第n位选被3模后余数为0的数的个数)
我们先看看当p == 0时,是怎样得到结果的:有三种情况:第一种情况:前n-1个数的和取模后余数为0且第n个数选余数为0的数,这样才能保证前n位之和取模后余数为0(也就是p);第二种情况:前n-1个数的和取模后余数为1且第n个数选余数为2的数,这样才能保证前n位之和取模后余数为0(也就是p);第三种情况同理可得,p == 1和p == 2也同理可得。
 
3.现在的问题关键是:第n位选被3模后余数为p的数的个数怎么算:我们可以思考这样一个问题:1-x中,被3取模后余数为p的数的个数 fp(x) 是多少?假如我们解决这个问题,就可以用 fp(r) - fp(l-1) 来算出答案了。因为p的选择不多(p = 0, 1, 2),所以我们直接分类讨论就行了:
1-x中,被3取模后余数为0的数的个数:f0(x) = x / 3
1-x中,被3取模后余数为1的数的个数:f1(x) = (x + 2) / 3
1-x中,被3取模后余数为2的数的个数:f2(x) = (x + 1) / 3
 
4.自己注意初始化问题。
 
 
AC代码:
 1 #include <cstdio>
2 #include <cstring>
3 #include <iostream>
4 #include <cmath>
5 #include <algorithm>
6 using namespace std;
7 const long long mod = 1e9+7;
8 const long long maxn = 2e5+5;
9 long long n, l, r;
10
11 long long dp[5][maxn];
12
13 const int yu[3][3] = {{0, 2, 1}, {1, 0, 2}, {2, 1, 0}};
14
15
16 int main(){
17 cin >> n >> l >> r;
18 int t;
19
20 //注意初始化
21 for(int i = 0; i < 3; i++){
22 t = (3-i)%3;
23 dp[i][1] = ( (r+t)/3-(l+t-1)/3 );
24 }
25
26
27 for(int i = 2; i <= n; i++){
28 for(int k = 0; k < 3; k++){
29 for(int p = 0; p < 3; p++){
30 t = (3-yu[k][p]) % 3;
31 dp[k][i] += dp[p][i-1]*( (r+t)/3-(l+t-1)/3 ) % mod;
32 dp[k][i] %= mod;
33 }
34 }
35 }
36
37 cout << dp[0][n] << endl;
38
39 return 0;
40 }
 
 
 
 

最新文章

  1. 第12章 Linux系统管理
  2. hibernate一级缓存的源码初窥
  3. MySQL 常见的sql命令
  4. ffmpeg-20160718-git-bin.7z
  5. PBcR - 纠错及组装算法
  6. 使用eclipse集成开发环境开发第一个嵌入式Linux驱动
  7. SQL技术内幕-7 varchar类型的数字和 int 类型的数字的比较+cast的适用
  8. Mac下配置idk
  9. 部署解决方案包 (SharePoint Server 2010)
  10. android_handler(三)
  11. JDBC中rs.beforeFirst()
  12. 开发板访问linux方法
  13. java学习笔记01-环境配置
  14. mysql 开发进阶篇系列 18 MySQL Server(innodb_buffer_pool_size)
  15. SpringBoot------自动装配Mapper报错
  16. 《图解HTTP》
  17. android 调试Installation failed with message INSTALL_FAILED_USER_RESTRICTED: Install canceled by user.
  18. 用css 实现凹陷的线条
  19. JsonConvert.DeserializeAnonymousType
  20. MYSQL数据库里面的所有密码批量MD5加密

热门文章

  1. codeforces 5C
  2. JSON简单理解
  3. mssql数据库提权(xp_cmdshell)
  4. es6 curry function
  5. Set-Cookie &amp; Secure &amp; HttpOnly &amp; SameSite
  6. Android Studio show whitespace &amp; Android studio 设置注释缩进
  7. NGK新加坡峰会:超级节点和开源代码为DeFi生态带来新曙光!
  8. 【PY从0到1】 一文掌握Pandas量化进阶
  9. Java并发包源码学习系列:同步组件CountDownLatch源码解析
  10. 《进击吧!Blazor!》第一章 5.组件开发