题目链接:​​http://codeforces.com/problemset/problem/1105/C​

题目描述

聪聪有一个长度为 \(n\) 的整数数组 \(a\) ,并且这个数组具有如下属性:

  1. 数组中每个元素的大小都在 \(l\) 和 \(r\) 之间(包含 \(l\) 和 \(r\) );
  2. 数组中所有元素之和能被 \(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;
}