D:
这是一道好dp(对我来说)
我做的时候有想过正解那个方式当考虑到可能有重复就没有深入思考了
假设dp[i][j]代表第i次j的方案数
此时我们可以发现dp[i][j]=sum(dp[i-1][j-p*(k+i-1)]);
对于这个式子3维dp一定超时 那我们考虑如何优化
既然p会导致我们超时 那么我们就想能不能将其优化掉
我们考虑一步一步扩展 当一个数边维dp[i-1][j]->dp[i][j+a]
他走两步的话就是dp[i-1][j+2*a]
那么我们此时可以发现dp[i][j+2*a]可以由dp[i][j+a]来表达而不是利用dp[i-1][j]
同时我们要注意p[i][j]均可以通过dp[i-1][j-a]来表达
当然我们也可以换一个角度来考虑:
就是完全背包的角度来处理这个p:
对于每一次来说p代表的是取多少个 和完全背包类似
那么此时我们考虑完全背包的转移:
for(int i=begin;i<=n;i++)
dp[i]+=dp[i-a[i]];
可以发现我们将a[i]变为此时的步长就好了 不过要注意的是 此时我们题目要求必须是经过k,k+1
k+2.....转移过来 所以我们此时不能直接在上一个的基础上继续dp 因为这样的话可能会遗漏掉其中的一步 所以我们要使用滚动数组
所以对于dp[i][j]和dp[i-1][j]之间的关系转换优化空间的话就利用滚动数组就好了
(以后写dp要从完备的表达式考虑)不然像我这个直接dp[j]考虑的话可能想一辈子都不明白
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<cmath>
#include<map>
#include<vector>
#include<set>
#define int long long
using namespace std;
const int maxn = 2e5 + 10;
const int mod = 998244353;
int a[maxn], n, k;
int f[2][maxn];
int ans[maxn];
vector<int>v;
void solve()
{
cin >> n >> k;
f[0][0] =1;
int sum = 0;
for (int i = 1; i <= n; i++)//时间
{
sum += (k + i - 1);
int a = i + k-1;
if(sum>n)
break;
memset(f[i & 1], 0, sizeof f[i & 1]);
for (int j = sum; j <= n; j++)
{
if (j >= a)
f[i & 1][j] = (f[i - 1 & 1][j - a] + f[i & 1][j - a]) % mod;
}
for (int j = 1; j <= n; j++)
ans[j] = (ans[j] + f[i & 1][j]) % mod;
}
for (int i = 1; i <= n; i++)
cout << ans[i] << ' ';
}
signed main()
{
int t=1;
//cin >> t;
while (t--)
solve();
}
可能会被卡 建议加个快读
C:
这道题其实思路挺明确的 就是我写的比较差 一直没有debug出来:(这告诉我们代码要写好,写清晰)
思路的话:
我们可以发现 他的走法无非就是蛇形 或者蛇性走到一半的话就直接开始走到头在掉头回来
所以我们考虑这两种如何处理
对于第一种的话 我们就模拟一下就好了 对于第二种的话
我们要在之前的基础上考虑从该点走到n和n走回来的时间
对于从该点走到n的话 我们发现假如此时有一个a[i][j]大于我们此时的时间的话 我们的时间就会变为a[i][j]+1 然后从该点往后的话就要n-j的时间到n了(假如后面没有阻挡的话)
所以此时我们就可以抛弃之前维护的蛇形时间直接考虑a[i][j]+1+n-j的最大值了:(因为假如时间不够的话会被强行提高到a[i][j]+1)
证明正确性:
考虑上述 假如有一个a[i][j+k]大于a[i][j]的话 且a[i][j]+n+1-j>a[i][j+k]+1+n-(j+k) 的话我们是否可以以a[i][j]+n+1-j 为标准来判断时间为多少呢:(主要是证明我们后面n-j步可以正常走不会的停留)
考虑上述不等式:化简得到:a[i][j]+k>a[i][j+k] 所以和前提不符合 所以此时a[i][j]+n+1-j<a[i][j+k]+1+n-(j+k)恒成立 所以当我们到达一个a[i][j]+n+1-j最大的j时 后面一定会畅通无阻
当a[i][j+k]小于a[i][j]的话恒显然成立(后面n-j步可以正常走不会的停留)
考虑走回来:
同理我们找a[i^1][j]+j-(y)+1最大的值:我们这时候将其看为一条直线的话就可以发现他和走到n的情况一样 不用理会t
接下来就是码分的问题了 假如写的好的话会很快 假如写的差的话要debug一天
这里用的是Educational Codeforces Round 133 (Rated for Div. 2) A-D - 哔哩哔哩 (bilibili.com)
的代码写法 (我连蛇形走都走不好qwq)
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 2e5 + 10;
const int inf = 1e18;
int a[2][maxn];
int vis[2][maxn];
int maxx1[2][maxn];
int maxx2[2][maxn];
void solve()
{
int n;
cin >> n;
for(int j=0;j<=1;j++)
for (int i = 1; i <= n; i++)
{
cin >> a[j][i];
vis[j][i] = 0;
}
for (int j = 0; j <= 1; j++)
{
maxx1[j][n + 1] =maxx2[j][n+1]= -inf;
for (int i = n; i >= 1; i--)
{
maxx1[j][i] = max(maxx1[j][i + 1], a[j][i] - i);
maxx2[j][i] = max(maxx2[j][i + 1], a[j][i] + i);
}
}
int x = 0, y = 1;
int ans = 1e18;
vis[0][1] = 1;
int t = 0;//代表蛇形走
while (1)
{
if (!vis[x ^ 1][y])//此时该行的另一侧还没有被遍历
{
int tmp = max(t + n - y, maxx1[x][y + 1] + n+1);
ans = min(ans, max(tmp + n - y + 1, maxx2[x ^ 1][y] - y + 1));
x ^= 1;
}
else
{
y++;
}
if (y > n)
break;
vis[x][y] = 1;
t = max(t + 1, a[x][y] + 1);
}
cout << min(t, ans) << endl;
}
signed main()
{
int t;
cin >> t;
while (t--)
solve();
}