CF1105C Ayoub and Lost Array 题解 动态规划
题目链接:http://codeforces.com/problemset/problem/1105/C
题目描述
聪聪有一个长度为 \(n\) 的整数数组 \(a\) ,并且这个数组具有如下属性:
- 数组中每个元素的大小都在 \(l\) 和 \(r\) 之间(包含 \(l\) 和 \(r\) );
- 数组中所有元素之和能被 \(3\) 整除。
但是聪聪不小心把这个数组中的元素全部消除掉了。
聪聪现在只记住了 \(n\) 、\(l\) 和 \(r\) ,现在聪聪想知道满足条件的数组有多少个。
因为答案可能很大,所以你只需要输出答案除以 \(1000000007\) 的余数就可以了。
输入格式
输入的第一行 \(n\), \(l\) 和 \(r\)( \(1 \le n \le 2 \times 10^5, 1 \le l \le r \le 10^9\) ),分别表示数组的大小和数组元素的范围。
输出格式
输出答案除以 \(10^9+7\) 的余数。
样例输入1
2 1 3
样例输出1
3
样例输入2
3 2 2
样例输出2
1
样例输入3
9 9 99
样例输出3
711426616
样例解释
对于样例1,可能组成的数组有:\([1,2],[2,1],[3,3]\) ;
对于样例2,可能组成的数组有:\([2,2,2]\) 。
题目分析
本题涉及知识点:动态规划。
我们需要开一个数组 \(f[n][3]\) 。
其中 \(f[i][j]\) 表示 从 \(a[1]\) 到 \(a[i]\) 之和除以 \(3\) 余 \(j\) 的所有方案数。
具体分析
我们假设区间 \([l,r]\) 范围内:
- 除以 \(3\) 的余数为 \(0\) 的数的个数是 \(\Delta_0\);
- 除以 \(3\) 的余数为 \(1\) 的数的个数是 \(\Delta_1\);
- 除以 \(3\) 的余数为 \(2\) 的数的个数是 \(\Delta_2\)。
则:
- \(f[1][0] = \Delta_0\);
- \(f[1][1] = \Delta_1\);
- \(f[1][2] = \Delta_2\)。
并且,对于任意一个 \(i \gt 1\),有:
- \(f[i][0] = f[i-1][0] \times \Delta_0 + f[i-1][1] \times \Delta_2 + f[i-1][2] \times \Delta_1\)
- \(f[i][1] = f[i-1][0] \times \Delta_1 + f[i-1][1] \times \Delta_0 + f[i-1][2] \times \Delta_2\)
- \(f[i][2] = f[i-1][0] \times \Delta_2 + f[i-1][1] \times \Delta_1 + f[i-1][2] \times \Delta_0\)
上面的状态转移方程在具体实现时可以写地简化一些(详见下面的代码)。最终输出 \(f[n][0]\) 就是答案。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
#define MOD 1000000007LL
const int maxn = 200020;
int n, l, r;
long long f[maxn][3];
int main() {
cin >> n >> l >> r;
f[0][0] = 1;
for (int i = 1; i <= n; i ++) {
for (int j = 0; j < 3; j ++) {
long long num = (r+j)/3 - (l-1+j)/3;
for (int k = 0; k < 3; k ++) {
f[i][(k+j)%3] = (f[i][(k+j)%3] + f[i-1][k]*num) % MOD;
}
}
}
cout << f[n][0] << endl;
return 0;
}