CF1105C Ayoub and Lost Array 题解 动态规划

题目链接: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;
}
posted @ 2020-01-17 14:28  quanjun  阅读(322)  评论(0编辑  收藏  举报