E - Distance Sequence (前缀和优化dp

E - Distance Sequence
思路:
d p [ i ] [ j ] dp[i][j] dp[i][j] 表示考虑前 i i i 个数,最后一个数为 j j j 时的方案数
显然 d p [ i ] [ j ] + = d p [ i − 1 ] [ l ] dp[i][j]+=dp[i-1][l] dp[i][j]+=dp[i1][l] l l l 为前 i − 1 i-1 i1 个数中以 l l l 结尾并且与 j j j 相接合法的情况
j − l ≥ k j-l\geq k jlk,即 1 ≤ l ≤ j − k 1\leq l \leq j-k 1ljk
l − j ≥ k l-j\geq k ljk,即 j + k ≤ l ≤ m j+k \leq l \leq m j+klm
显然可以维护一个前缀和优化
(注意需要特判 K = 0 K=0 K=0 的情况
code:

#include
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
ll dp[1009][5009];

void work()
{
	int k;
	cin >> n >> m >> k;
	if(k == 0){
		ll ans = 1;
		for(int i = 1; i <= n; ++i)
			ans = ans * m % mod;
		cout << ans;
		return;
	}
	vector <ll> sum(m + 1, 0);
	for(int i = 1; i <= m; ++i) {
		dp[1][i] = 1;
		sum[i] = sum[i-1] + dp[1][i];
	}
	for(int i = 2; i <= n; ++i){
		vector <ll> now_sum(m + 1, 0);
		for(int j = 1; j <= m; ++j){
			if(j - k >= 1)
				(dp[i][j] += sum[j - k]) %= mod;
			//for(int l = 1; l <= j - k; ++l)
			//	(dp[i][j] += dp[i-1][l]) %= mod;
			if(j + k <= m)
				(dp[i][j] += (sum[m] - sum[j+k-1] + mod) % mod) %= mod;
			//for(int l = j + k; l <= m; ++l)
			//	(dp[i][j] += dp[i-1][l]) %= mod;
		}
		for(int j = 1; j <= m; ++j) now_sum[j] = (now_sum[j-1] + dp[i][j]) % mod;
		sum = now_sum;
	}
	cout << sum[m];
}

int main()
{
	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

改进 d p dp dp 过程就可以不用单独特判了,但是也要想到 k = = 0 k==0 k==0 的时情况,然后修改转移方程
code:

#include
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m, k;
ll dp[1009][5009];
 
void work()
{
	cin >> n >> m >> k;
	
	vector <ll> sum(m + 1, 0);
	for(int i = 1; i <= m; ++i) {
		dp[1][i] = 1;
		sum[i] = sum[i-1] + dp[1][i];
	}
	
	for(int i = 2; i <= n; ++i){
		vector <ll> now_sum(m + 1, 0);
		for(int j = 1; j <= m; ++j){
			int l = max(1ll, j - k + 1), r = min(m, j + k - 1);
			if(k){
				dp[i][j] = (sum[m] - (sum[r] - sum[l-1]) + mod) % mod;
			}
			else {
				dp[i][j] = sum[m];
			}
		}
		for(int j = 1; j <= m; ++j) now_sum[j] = (now_sum[j-1] + dp[i][j]) % mod;
		sum = now_sum;
	}
	cout << sum[m];
}

int main()
{
	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

想到 k = 0 k=0 k=0 时再改变转移方程,对于我这种菜鸡根本做不到啊,于是就出现了下边的写法,就可以完全不用考虑 K K K 的值了,只需要保证每次转移都合法即可(也就是 r > = l − 1 r>=l-1 r>=l1
code:

#include
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m, k;
ll dp[1009][5009];
// 考虑前i个数,最后一个数为j
void work()
{
	cin >> n >> m >> k;
	
	vector <ll> sum(m + 1, 0);
	for(int i = 1; i <= m; ++i) {// 单独一个数1-m都可以
		dp[1][i] = 1;
		sum[i] = sum[i-1] + dp[1][i];
	}
	
	for(int i = 2; i <= n; ++i){
		vector <ll> now_sum(m + 1, 0);
		for(int j = 1; j <= m; ++j){
			int l = max(1ll, j - k + 1), r = min(m, j + k - 1);// 不合法的区间
			dp[i][j] = sum[m];
			if(r >= l - 1) {// 不合法区间存在就需要删
				dp[i][j] = (dp[i][j] - (sum[r] - sum[l-1]) + mod) % mod;
			}
		}
		for(int j = 1; j <= m; ++j) now_sum[j] = (now_sum[j-1] + dp[i][j]) % mod;
		sum = now_sum;
	}
	cout << sum[m];
}

int main()
{
	ios::sync_with_stdio(0);
//	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

你可能感兴趣的:(线性dp,算法,图论,c++)