CF1811G2 Vlad and the Nice Paths (hard version) 题解
有一种比较暴力,不需要组合数的 DP。
先考虑在 G1 的限制(即 )下怎么做。
有一个比较显然的 DP, 表示前 个,长度为 ,最后一个的颜色为 。转移时可以朴素地 转移,注意 时可以从 的任意一个 转过来,于是需要在 DP 的同时维护一下和以防复杂度退化。
回到 G2,考虑上文的状态就已经是三次方,首先要优化这个东西。这一步是容易的,因为 仅有 而来,滚动数组即可。
接着是转移过程。注意到上文中既有 时才真正进行了 DP 值的修改,于是不枚举 ,只枚举 即可。
然而这么写有个问题是,如果最终某个 的方案数是 的倍数,取模变成 ,我们会认为不存在这种长度,于是会在第 个点 WA。于是在 DP 时顺便记录一个 bool
数组表示这种情况是否有可能,转移和 DP 转移类似。
顺带一提:没注意到这个问题仍然能过 G1,可能是数据比较弱。
#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
const long long MOD = 1e9 + 7;
int t, n, m, a[N];
long long f[N][N];
long long sum[N];
bool cb[N][N];
bool cc[N];
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
cin >> t;
while (t--)
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= n; j++)
{
f[i][j] = 0ll, cb[i][j] = 0, sum[i] = 0ll, cc[i] = 0;
}
}
f[0][0] = 1ll;
sum[0] = 1ll;
cb[0][0] = 1;
cc[0] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = n; j >= 1; j--)
{
sum[j] = (sum[j] - f[j][a[i]] + MOD) % MOD;
f[j][a[i]] = (f[j][a[i]] + f[j - 1][a[i]]) % MOD;
cb[j][a[i]] |= cb[j - 1][a[i]];
if (j % m == 1)
{
f[j][a[i]] = (f[j][a[i]] + sum[j - 1]) % MOD;
cb[j][a[i]] |= cc[j - 1];
f[j][a[i]] = (f[j][a[i]] - f[j - 1][a[i]] + MOD) % MOD;
}
sum[j] = (sum[j] + f[j][a[i]]) % MOD;
cc[j] |= cb[j][a[i]];
}
}
long long ans = 0ll;
for (int j = 0; j <= n; j += m)
{
long long sum = 0ll;
bool flg = 0;
for (int k = 0; k <= n; k++)
{
sum = (sum + f[j][k]) % MOD;
flg |= cb[j][k];
//cout << "!!!: " << j <<" " << k << " " << f[n][j][k] << "\n";
}
if (flg)
{
ans = sum;
}
}
cout << ans << "\n";
}
return 0;
}