1. 程式人生 > 實用技巧 >【Codeforces Round #695 (Div. 2) D】Sum of Paths

【Codeforces Round #695 (Div. 2) D】Sum of Paths

題目連結

連結

翻譯

可以從陣列中任意一個位置開始出發走一條路徑,每一步可以往走到相鄰的一個格子(左或右)。但是不能超過邊界。

問你所有不同的長度為 \(k+1\) 的路徑的和是多少。

然後要支援更新操作實時回答這個路徑和。

題解

\(n\)\(k\) 都只有 \(5000\),其實是比較容易往 \(DP\) 上面想的。

實時更新的話,只要能夠知道最後每個數字在答案中的貢獻 \(cnt_i\)(出現了幾次),要做到實時更新也不難。

直接在原來答案的基礎上加上 \((x-a[i])*cnt_i\) 即可。

怎麼求這個 \(cnt_i\) 呢?

\(DP\)

\(dp[i][j]\) 表示以第 \(i\)

個數字結尾的長度為 \(j\) 的序列有多少個。

顯然有轉移方程 \(dp[i][j] = dp[i-1][j-1] + dp[i+1][j-1]\), 且 \(dp[i][0] = 1\)

同時,會發現這個 \(dp[i][j]\) 除了可以理解為上面的意思,還能表示以第 \(i\) 個數字開始的長度為 \(j\) 的序列有多少個。

因為向左和向右是相反對稱的嘛(理解這個角度的定義很重要)。

這有什麼用呢?我們可以用它來求出陣列 \(cnt[N][K]\),其中 \(cnt[i][j]\) 表示的是長度為 \(k\) 的序列, 在第 \(j\) 步走到了

\(i\) 位置的序列個數。則累加 \(cnt[i][0..k]\)

就是 \(a[i]\) 在所有長度為 \(k\) 的路徑中的貢獻了。

也即,第 \(0,1,2,3...k\) 步走到了 \(a[i]\) 的長度為 \(k\) 的路徑數目。

因為 \(dp[i][j]\) 既能表示開始,也能表示結束路徑個數。那麼 \(dp[i][j]+dp[i][k-j]\) 不就是我們要求的 \(cnt[i][j]\) 了嗎。

也即 \(cnt[i][j] = dp[i][j] + dp[i][k-j]\) (以 \(i\) 為結尾的長度為 \(j\) 的序列,加上一個以 \(i\) 開始的長度為 \(k-j\) 的序列。

拼起來就是長度為 \(k\) 的了。

\(cnt[i][j]\)

的時候做個字首和就好。

這樣就知道每個數字對最後答案的貢獻了,按照我一開始說的方法實時更新答案即可。

程式碼

#include <bits/stdc++.h>
#define LL long long
using namespace std;

const int N = 5000;
const LL MOD = 1e9 + 7;
const int K = 5000;

int n, k, q;
LL a[N + 10];
LL dp[N + 10][K + 10];
LL times[N + 10][K + 10];

int main() {
	#ifdef LOCAL_DEFINE
		freopen("in.txt", "r", stdin);
	#endif
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> k >> q;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		dp[i][0] = 1;
	}
	for (int j = 1; j <= k; j++) {
		for (int i = 1; i <= n; i++) {
			dp[i][j] = dp[i - 1][j - 1] + dp[i + 1][j - 1];
			dp[i][j] %= MOD;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= k; j++) {
			times[i][j] = dp[i][j] * dp[i][k - j]%MOD;
			if (j > 0) {
				times[i][j] = (times[i][j] + times[i][j - 1]) % MOD;
			}
		}
	}

	LL ans = 0;
	for (int i = 1; i <= n; i++) {
		ans = (ans + times[i][k] * a[i] % MOD) % MOD;
	}

	while (q--) {
		int i, x;
		cin >> i >> x;
		ans = ans + ((x - a[i]) % MOD+MOD)%MOD * times[i][k] % MOD;
		ans %= MOD;
		cout << ans << endl;
		a[i] = x;
	}
	return 0;
}