LastWhisper最后的耳语

算法杂记 2023/02/16

WKL && YZY·2023-02-16 23:07·30 次阅读

算法杂记 2023/02/16

算法杂记 2023/02/16#

今天分享的是 Codeforce 上的一道 2000 分的 动态规划 + 计数 题。目前的目标是从紫冲橙。

D. Different Arrays 2000#

题面翻译

给你一个有 n 个元素的序列,你需要进行 n2 次操作。

对于第 i 次操作,你可以选择让 aiai+1ai+2+ai+1 或者可以选择让 ai+ai+1ai+2ai+1

问最后能产生多少个不同的序列。

题目描述

You are given an array a consisting of n integers.

You have to perform the sequence of n2 operations on this array:

  • during the first operation, you either add a2 to a1 and subtract a2 from a3 , or add a2 to a3 and subtract a2 from a1 ;
  • during the second operation, you either add a3 to a2 and subtract a3 from a4 , or add a3 to a4 and subtract a3 from a2 ;
  • ...
  • during the last operation, you either add an1 to an2 and subtract an1 from an , or add an1 to an and subtract an1 from an2 .

So, during the i -th operation, you add the value of ai+1 to one of its neighbors, and subtract it from the other neighbor.

For example, if you have the array [1,2,3,4,5] , one of the possible sequences of operations is:

  • subtract 2 from a3 and add it to a1 , so the array becomes [3,2,1,4,5] ;
  • subtract 1 from a2 and add it to a4 , so the array becomes [3,1,1,5,5] ;
  • subtract 5 from a3 and add it to a5 , so the array becomes [3,1,4,5,10] .

So, the resulting array is [3,1,4,5,10] .

An array is reachable if it can be obtained by performing the aforementioned sequence of operations on a . You have to calculate the number of reachable arrays, and print it modulo 998244353 .

输入格式

The first line contains one integer n ( 3n300 ).

The second line contains n integers a1,a2,,an ( 0ai300 ).

输出格式

Print one integer — the number of reachable arrays. Since the answer can be very large, print its remainder modulo 998244353 .

样例 #1

样例输入 #1

Copy
4 1 1 1 1

样例输出 #1

Copy
3

样例 #2

样例输入 #2

Copy
5 1 2 3 5 0

样例输出 #2

Copy
7

显然,这题是 dp 题,这种计数题肯定是 dp 的思路。

从题解出发,假设我们使用一个最简单的思路,假设我们当前操作到第 i 个元素,我们操作完后前 i 个元素就已经固定,第 i+2 个元素后的元素也不会被影响。那么假设 dp 数组为 dpi,x,y,其中 i 表示第 i 次操作, x 表示第 i 个元素, j 表示第 i+1个元素。假设我们把第 ai 加到 ai+1 上则我们应该将状态转移到 dpi+1,y,ai+1+y,否则我们需要转移到 dpi+1,y,ai+1y

这样思路非常清晰,考虑到时间复杂度需要 O(n3×max(A)2),我们需要进行一步优化。

可以发现,x 位可以省略。并且操作位 i 可以压缩到 2 维。假设 i=0 表示当前状态, i=1 表示被更新到状态。

那么现在 dp 方程变为: dpi,x 代表操作完第 i 个元素后,ai+1 的值为 x 的可能情况数。

假设我们枚举的 valai 的可能情况,由于 val[N,N] 其中 N 代表一个 base offset。所以我们需要让他 加上 N 来保证始终为正数。

  • 假设我们考虑的是添加 valai+1 处,所以原始的结果应该是:a[i+1]+val。 但是考虑到偏置 N 的存在:

dp[1][(a[i+1]+val+N]+=dp[0][val+N]

  • 假设我们考虑的是减去 valai+1 处,所以原始的结果应该是:a[i+1]+val。但是考虑到偏置 N 的存在:

dp[1][a[i+1]val+N]+=dp[0][val+N]

同时,我们需要注意的是,如果 val==0 实际上两次转移是相同的,这样会重复,我们需要特殊考虑下这种情况,当 val=0 时,我们只需要转移一次。

最后统计下 ans=dp[0][] 即为答案。算法的时间复杂度为:O(n×max(A)2)

Copy
#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 ,在整个题目描述上非常类似,可以之后也做一下。

posted @   Last_Whisper  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示
目录