Processing math: 100%
(Analysis by Andi Qu, Arpan Banerjee, Benjamin Qi)

Case 1: N is even

Note that if some starting N-tuple can make all cows end up with hunger level x, then it can also make all cows end up with hunger level 0 (by feeding each consecutive pair of cows x bags of corn). So it suffices to find the number of N-tuples that can be converted to all 0's.

If we want to check whether a specific N-tuple can be converted to all 0's, then the following pseudocode suffices:

for (i in 2..N) {
  h[i] -= h[i-1]
  assert(h[i] >= 0)
}
assert(h[N] == 0)

The reasoning behind this is that if h1,,hi2 are all equal to zero already, then the only way to make hi1=0 is by feeding hi1 bags of corn to each of cows i1 and i.

Motivated by this, we can define a DP array where dp[i][j] is the number of ways to choose h1hi satisfying hkHk for all 1ki such that after the i-th iteration of the loop in the pseudocode above, hi=j. Then

dp[i][j]=Hijx=0dp[i1][x].

That is, if hi1=x and hi=j+xHi before the i-th iteration of the loop, then hi1=0 and hi=j after the i-th iteration of the loop. The final answer will be dp[N][0].

A straightforward implementation of this algorithm runs in O(N(maxH)2) time, which is already fast enough. However, we can speed this up by using prefix sums for the transition since we are summing over a contiguous range. Specifically, define pref[i][j]=jx=0dp[i][x]; then dp[i][j]=pref[i1][Hij]. This results in a runtime of O(N(maxH)).

Andi's code:

#include <iostream>
#include <algorithm>
#include <cstring>
typedef long long ll;
using namespace std;
 
const ll MOD = 1e9 + 7;
 
int main() {
    cin.tie(0)->sync_with_stdio(0);
    int n, h[101];
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> h[i];
    ll dp[1001], pref[1001];
    for (int i = 0; i <= h[1]; i++) pref[i] = i + 1;
    for (int i = h[1] + 1; i <= 1000; i++) pref[i] = h[1] + 1;
    for (int i = 2; i <= n; i++) {
        memset(dp, 0, sizeof dp);
        for (int j = 0; j <= h[i]; j++) {
            dp[j] = pref[h[i] - j];
            if (dp[j] >= MOD) dp[j] -= MOD;
        }
        for (int j = 0; j <= 1000; j++) {
            pref[j] = dp[j];
            if (j) pref[j] += pref[j - 1];
            if (pref[j] >= MOD) pref[j] -= MOD;
        }
    }
    cout << pref[0];
    return 0;
}

Case 2: N is odd

In this case, it is not true that if a starting N-tuple can make all cows end up with hunger level x, then it can also make all cows end up with hunger level 0. In fact, if a starting N-tuple can make all cows end up with hunger level x, then x must be unique (as mentioned in the Bronze analysis). So it suffices to sum the number of starting N-tuples over all final hunger levels x satisfying 0xminH.

To count this quantity for a fixed x, consider subtracting x from all hi and Hi. Then the problem reduces to converting tuples to all 0's, which was described above.

The final time complexity for this case is maxH times that of the complexity for the previous case, or O(N(maxH)2).

Andi's code:

#include <iostream>
#include <algorithm>
#include <cstring>
typedef long long ll;
using namespace std;
 
const ll MOD = 1e9 + 7;
 
int main() {
    cin.tie(0)->sync_with_stdio(0);
    int n, h[101];
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> h[i];
    int mn = *min_element(h + 1, h + n + 1);
    ll dp[1001], pref[1001], ans = 0;
    do { 
        // on x-th iteration of loop, count number of tuples
        // that can make all heights equal to x-1
        for (int i = 0; i <= h[1]; i++) pref[i] = i + 1;
        for (int i = h[1] + 1; i <= 1000; i++) pref[i] = h[1] + 1;
        for (int i = 2; i <= n; i++) {
            memset(dp, 0, sizeof dp);
            for (int j = 0; j <= h[i]; j++) {
                dp[j] = pref[h[i] - j];
                if (dp[j] >= MOD) dp[j] -= MOD;
            }
            for (int j = 0; j <= 1000; j++) {
                pref[j] = dp[j];
                if (j) pref[j] += pref[j - 1];
                if (pref[j] >= MOD) pref[j] -= MOD;
            }
        }
        ans += pref[0]; 
        if (ans >= MOD) ans -= MOD;
        for (int i = 1; i <= n; i++) h[i]--;
    } while (n % 2 && mn--);
    cout << ans;
    return 0;
}

Interestingly, each DP transition is equivalent to reversing a subarray of the previous prefix sum array, so we can code a very short (and fast) solution using functions from C++'s standard library.

#include <algorithm>
#include <cstdio>
#include <numeric>
using namespace std;
 
int mod_add(int x, int y) {
    int res = x + y;
    if (res >= 1000000007) res -= 1000000007;
    return res;
}
 
int main() {
    int n, h[101], mn, mx, dp[1001], ans = 0;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%d", h + i);
    mn = *min_element(h, h + n), mx = *max_element(h, h + n);
    do {
        fill(dp, dp + mx + 1, 1);
        for (int i = 0; i < n; i++) {
            reverse(dp, dp + h[i] + 1);
            fill(dp + h[i] + 1, dp + mx + 1, 0);
            partial_sum(dp, dp + mx + 1, dp, mod_add);
        }
        ans = mod_add(ans, dp[0]);
        for (int i = 0; i < n; i++) h[i]--;
    } while (n % 2 && mn-- && mx--);
    printf("%d\n", ans);
    return 0;
}