题目链接: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 imes 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] imes Delta_0 + f[i-1][1] imes Delta_2 + f[i-1][2] imes Delta_1)
- (f[i][1] = f[i-1][0] imes Delta_1 + f[i-1][1] imes Delta_0 + f[i-1][2] imes Delta_2)
- (f[i][2] = f[i-1][0] imes Delta_2 + f[i-1][1] imes Delta_1 + f[i-1][2] imes 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;
}