UPD:修復了原來取值范圍不嚴謹的問題。
把 \(a_i\) 和 \(\frac{a_i + a_{i + 1} - b_i}{2}\) 做個比較,發現前者小於等於后者的條件為 \(a_{i + 1} - a_{i} \ge b_i\)。
把 \(a_{i + 1}\) 和 \(\frac{a_i + a_{i + 1} + b_i}{2}\) 做個比較,發現前者大於等於后者的條件為 \(a_{i + 1} - a_i \ge b_i\)。
發現這兩個條件是一樣的,也就是說,一次操作的本質是:
- 如果 \(a_{i + 1} - a_i \ge b_{i}\),則不會發生變化。
- 如果 \(a_{i + 1} - a_i < b_i\),則會發生變化。
更近一步的,如果發生了一次變化,那相鄰兩個數的差會變成什么呢?
也就是說,相鄰的兩個數的差,從原本的 \(< b_i\) 變成了 $ = b_i$,且這兩個數的和不變。
設 \(f_{1 \cdots n}\) 表示最終收斂的數組,必然對任意 \(i\) 都存在 \(f_{i+1} - f_i \ge b_i\)。
並且如果 \(f_{i + 1} - f_i > b_i\),則整個過程中,\(a_i\) 和 \(a_{i + 1}\) 之間就從來沒有發生過賦值操作。
這是因為,如果發生了操作,那么這次操作后 \(f_{i + 1} - f_{i}\) 就會變得等於 \(b_i\),而且我們知道兩個數的差只能從小於 \(b_i\) 變成等於 \(b_i\),而不能變成大於 \(b_i\),所以最終無法滿足 \(f_{i + 1} - f_i > b_i\) 的條件。
以滿足這種嚴格大於關系的點 \(i\) 為斷點,可以發現,\(a_{1 \cdots i}\) 和 \(a_{i + 1 \cdots n}\) 是完全互相不影響的。
現在要解決最棘手的一個問題,如何求出 \(f_1\) 呢?
假設我們知道了第一個斷點的位置是 \(p\),那可以得到一個方程組:
將 \(f\) 看成未知數,則有 \(p\) 個未知數,\(p\) 個方程!於是我們可以手動解出 \(f_1\) 的值:
設 \(sa_p = \sum\limits_{i = 1}^{p}a_i, sb_p = \sum\limits_{i = 1}^{p - 1}(p - i)b_i\),則 \(f_1 = \frac{sa_p - sb_p}{p}\)。
但問題是,我們並不知道 \(p\) 是多少。正確的解決方法是,將每個位置都嘗試作為第一個端點,取解出來的 \(f_1\) 的最小值即可,也就是 \(f_1 = \min \left\{ \frac{s a_i - sb_i}{i} \right\}\)。
首先證明 \(f_1 \ge \min \left\{ \frac{sa_i - sb_i}{i} \right\}\),這是因為必然存在某個前綴 \(p\) 使得 \(f_1 = \frac{sa_p - sb_p}{p}\),所以 \(f_1\) 大於等於取任意 \(p\) 時的最小值。
然后證明 \(f_1 \le \min \left\{ \frac{sa_i - sb_i}{i} \right\}\),這是因為對於任意前綴 \(p\),如果 \(f_1 \ne \frac{sa_p - sb_p}{p}\),就說明必然存在 \(f_{i + 1} - f_i > b_i\ (i < p)\) 的位置。但因為 \(\sum f\) 是保持不變的,為了使得跨度變大只能減小初值,得出 \(f_1 < \frac{sa_p - sb_p}{p}\)。
回到原題,現在變成了一個計數問題。即,能找到多少個序列 \(a\),滿足 \(0 \le a_i \le c_i\),且 \(\min \left\{ \frac{s a_i - sb_i}{i} \right\} \ge x\)。
取 \(\min\) 轉化為任意,命題等價於,對任意前綴 \(i\),都要滿足:
當 \(q = 1\) 時,不等式右邊都是已知量,考慮一個 DP,用 \(f_{i, j}\) 表示前 \(i\) 項,和為 \(j\) 的方案數。因為合法的 \(a\) 必然是一個后綴,使用前綴和優化轉移即可,單次轉移 \(O(1)\)。
時間復雜度 \(O(qn^2m)\),\(m\) 是 \(a\) 的上限。
現在考慮多組詢問,不同的 \(x\) 是否存在等價關系呢?
- 如果存在 \(i\),滿足 $ i \cdot x + sb_i > i \cdot m$,即 \(x > \min\left\{\frac{i \cdot m - sb_i}{i}\right\}\),答案必然為 \(0\)。
- 如果任意 \(i\),滿足 \(i \cdot x + sb_i \le 0\),即 \(x \le \min\left\{ \frac{-sb_i}{i} \right\}\),答案必然為 \(\prod(c_i + 1)\)。
現在只需要特別考慮 \(x \in \left(\min\left\{ \frac{-sb_i}{i} \right\}, \min\left\{\frac{i \cdot m - sb_i}{i}\right\}\right]\) 即可。
於是只有 \(O(m)\) 種本質不同的詢問,全部預處理丟 map
里即可。時間復雜度 \(O(n^2m^2 + q \log m)\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105, MOD = 1e9 + 7;
int n, f[N * N], g[N * N], b[N], sb[N], c[N];
map<int, int> ans;
int Solve(int x)
{
fill(g, g + N * N, 1);
for(int i = 1, sumc = c[1], lim; i <= n; i++, sumc += c[i])
{
lim = i * x + sb[i]; memset(f, 0, sizeof(f));
for(int j = max(0, lim); j < N * N; j++) f[j] = (g[j] - (j - c[i] - 1 >= 0 ? g[j - c[i] - 1] : 0) + MOD) % MOD;
memset(g, 0, sizeof(g)); g[0] = f[0];
for(int j = 1; j < N * N; j++) g[j] = (g[j - 1] + f[j]) % MOD;
}
return g[N * N - 1];
}
int main()
{
ios::sync_with_stdio(false);
cin >> n;
int prod = 1;
for(int i = 1; i <= n; i++) { cin >> c[i]; prod = (ll)prod * (c[i] + 1) % MOD; }
for(int i = 1; i < n; i++) cin >> b[i];
for(int i = 1; i <= n; i++) for(int j = 1; j < i; j++) sb[i] += (i - j) * b[j];
int lb = 0, rb = INT_MAX, m = *max_element(c + 1, c + n + 1);
for(int i = 1; i <= n; i++) lb = min(lb, -((sb[i] - 1) / i + 1)), rb = min(rb, (i * m - sb[i] - 1) / i + 1);
for(int i = lb; i <= rb; i++) ans[i] = Solve(i);
int q; cin >> q;
while(q--)
{
int x; cin >> x;
if(x >= lb && x <= rb) cout << ans[x] << endl;
else if(x > rb) cout << 0 << endl;
else if(x < lb) cout << prod << endl;
}
return 0;
}