算法杂记 2023/02/16
算法杂记 2023/02/16#
今天分享的是 Codeforce 上的一道 2000
分的 动态规划 + 计数 题。目前的目标是从紫冲橙。
D. Different Arrays 2000
#
题面翻译
给你一个有 个元素的序列,你需要进行 次操作。
对于第 次操作,你可以选择让 且 或者可以选择让 且
问最后能产生多少个不同的序列。
题目描述
You are given an array consisting of integers.
You have to perform the sequence of operations on this array:
- during the first operation, you either add to and subtract from , or add to and subtract from ;
- during the second operation, you either add to and subtract from , or add to and subtract from ;
- ...
- during the last operation, you either add to and subtract from , or add to and subtract from .
So, during the -th operation, you add the value of to one of its neighbors, and subtract it from the other neighbor.
For example, if you have the array , one of the possible sequences of operations is:
- subtract from and add it to , so the array becomes ;
- subtract from and add it to , so the array becomes ;
- subtract from and add it to , so the array becomes .
So, the resulting array is .
An array is reachable if it can be obtained by performing the aforementioned sequence of operations on . You have to calculate the number of reachable arrays, and print it modulo .
输入格式
The first line contains one integer ( ).
The second line contains integers ( ).
输出格式
Print one integer — the number of reachable arrays. Since the answer can be very large, print its remainder modulo .
样例 #1
样例输入 #1
4 1 1 1 1
样例输出 #1
3
样例 #2
样例输入 #2
5 1 2 3 5 0
样例输出 #2
7
显然,这题是 dp
题,这种计数题肯定是 dp
的思路。
从题解出发,假设我们使用一个最简单的思路,假设我们当前操作到第 个元素,我们操作完后前 个元素就已经固定,第 个元素后的元素也不会被影响。那么假设 dp
数组为 ,其中 表示第 次操作, 表示第 个元素, 表示第 个元素。假设我们把第 加到 上则我们应该将状态转移到 ,否则我们需要转移到 。
这样思路非常清晰,考虑到时间复杂度需要 ,我们需要进行一步优化。
可以发现, 位可以省略。并且操作位 可以压缩到 维。假设 表示当前状态, 表示被更新到状态。
那么现在 dp
方程变为: 代表操作完第 个元素后, 的值为 的可能情况数。
假设我们枚举的 是 的可能情况,由于 其中 代表一个 base offset。所以我们需要让他 加上 来保证始终为正数。
- 假设我们考虑的是添加 到 处,所以原始的结果应该是:。 但是考虑到偏置 的存在:
- 假设我们考虑的是减去 到 处,所以原始的结果应该是:。但是考虑到偏置 的存在:
同时,我们需要注意的是,如果 实际上两次转移是相同的,这样会重复,我们需要特殊考虑下这种情况,当 时,我们只需要转移一次。
最后统计下 即为答案。算法的时间复杂度为:。
#include <bits/stdc++.h>
using namespace std;
#define DEBUG 0
#define vt std::vector
using ll = long long;
const int mod = 998244353;
const int N = 1e5 + 5;
void add(ll &x, ll y){
x += y;
while (x >= mod) x -= mod;
if (x < 0) x += mod;
}
ll dp[2][2 * N + 1];
void solve(){
int n;
std::cin >> n;
vt<int> a(n);
for (int i = 0; i < n; ++ i)
std::cin >> a[i];
// dp[i][j] := 操作第 i 个元素,a_{i+1} 最后是 j 的可能情况
memset(dp, 0, sizeof(dp));
dp[0][a[1] + N] = 1;
for (int i = 1; i + 1 < n; ++ i){
for (int j = -N; j <= N; ++ j){
if (dp[0][j + N] == 0) continue; // 避免了越界
add(dp[1][a[i + 1] + j + N], dp[0][j + N]);
if (j != 0)
add(dp[1][a[i + 1] - j + N], dp[0][j + N]);
}
swap(dp[0], dp[1]);
memset(dp[1], 0, sizeof(dp[1]));
}
ll ans = 0;
for (int i = 0; i < 2 * N; ++ i)
add(ans, dp[0][i]);
std::cout << ans << "\n";
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
// std::cin >> t;
while (t--) solve();
return 0;
}
最后这里有一个类似的题目 CF#808 Div2 D. Difference Array ,在整个题目描述上非常类似,可以之后也做一下。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人