依据值域的 \(O(n^2)\) 做法

这种做法只适用于这种值域小的题,下一种做法才是求等差数列的通解。

我们定义 \(f[i][j]\) 表示以 \(h_i\) 为最后一个数,公差为 \(j\) 的等差数列(长度 \(\ge 2\) ) 的个数。

接下来我们找每一个数前面的数,确定公差后转移即可。时间 \(O(n^2)\) 。

细节

表面上这道题很简单,但实际上细节非常多,一不小心就会挂,更何况这题的样例还比较大,不好调试。

首先,我们要把长度为 \(1\) 的等差数列个数记录到 \(ans\) 中(不加到 dp 数组里),即 \(n\) ,因为我们后面不会在考虑这些特殊的等差数列。

其次,我们在转移到时候,看似会这样写:

\[f[i][h[i]-h[j]] = f[i][h[i]-h[j]]+f[j][h[i]-h[j]]
\]

(省略本题原本要加的负数偏移量和取模操作)

其实并不对,因为我们转移的是它前面长度大于 \(1\) 的等差数列,在当时这些长度为 \(1\) 的不用算,但一旦转移到我们这里,这些长度为 \(1\) 的序列长度就变成 \(2\) 了,因此是要计算的。

考虑到每次转移,原先长度为 \(1\) 的只有一个,我们只要在转移的时候 \(+1\) 即可。

\[f[i][h[i]-h[j]] = f[i][h[i]-h[j]]+f[j][h[i]-h[j]]+1
\]

还有一个细节,就是在统计答案的时候,能否是:

\[ans = ans + f[i][j]
\]

实际上不行,因为每次遍历 \(j\) 如果都加一遍 \(f[i][j]\) ,那么如果在 \(i\) 前面有两个数一样,就会导致公差相同,这时候加 \(f[i][j]\) ,就会导致先更新的那种情况被加了两次,导致答案偏大。

解决办法就是把 \(ans\) 和 dp 数组一起转移:

\[f[i][h[i]-h[j]] = f[i][h[i]-h[j]]+f[j][h[i]-h[j]]+1
\]
\[ans = ans+f[j][h[i]-h[j]]+1
\]

于是就好了。

注意要取模,由于公差可能是负数,所以还要加一个偏移量。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353,eps=40005;
int n,h[1005];
ll ans=0,f[1005][100005];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
ans=(ans+1)%mod;
cin>>h[i];
for(int j=1;j<i;j++)
{
f[i][h[i]-h[j]+eps]=(f[i][h[i]-h[j]+eps]+f[j][h[i]-h[j]+eps]+1)%mod;
ans=(ans+f[j][h[i]-h[j]+eps]+1)%mod;
}
}
cout<<ans%mod;
return 0;
}

如果你不想考虑这么多,那么在全部转移完后再遍历一遍 dp 数组求方案也是可以的,这样就可以让 \(ans\) 直接加 \(f[i][j]\) 了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353,eps=40005;
int n,h[1005];
ll ans=0,f[1005][100005];
int main()
{
cin>>n;
ans=n;
for(int i=1;i<=n;i++)
{
cin>>h[i];
for(int j=1;j<i;j++)
{
f[i][h[i]-h[j]+eps]=(f[i][h[i]-h[j]+eps]+f[j][h[i]-h[j]+eps]+1)%mod;
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=100000;j++)
{
ans+=f[i][j];
ans%=mod;
}
}
cout<<ans%mod;
return 0;
}

基于数的多少的 \(O(n^2)\)~\(O(n^3)\) 的做法

定义 \(f[i][j]\) 表示前一项为 \(h_i\) ,最后一项为 \(h_j\) 的等差数列的个数。

转移的细节和上面基本一样,就不说明了。

很自然的转移方程:

\[f[j][i]=1+\sum f[k][j] (h[i]-h[j]=h[j]-h[k])
\]

时间 \(O(n^3)\) ,常数为 \(\frac{1}{6}\) ,稳过 \(10^3\) 数据。

优化就是把每个数的下标存进 unordered_map<int,vector<int>> 里,转移的时候避免多转移即可。

理想状态为 \(O(n^2)\) ,要是被卡哈希或者构造所有数相同的数据可能会到 \(O(n^3)\) 甚至带更大的常数。但概率比较小。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
int n,h[1005];
ll f[1005][1005],ans=0;
unordered_map<int,vector<int> >m;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>h[i];
m[h[i]].push_back(i);
}
ans=n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++)
{
f[j][i]=1;
vector<int>v=m[h[j]-(h[i]-h[j])];
for(int k=0;k<v.size()&&v[k]<j;k++)
{
int frm=v[k];
f[j][i]+=f[frm][j];
f[j][i]%=mod;
}
ans+=f[j][i];
ans%=mod;
}
}
cout<<ans%mod;
return 0;
}

Luogu P4933 大师 题解 [ 绿 ] [ 线性 dp ] [ dp 细节处理 ] [ 限制转移条件优化 ]的更多相关文章

  1. Luogu P4933 大师(dp)

    P4933 大师 题意 题目背景 建筑大师最近在跟着数学大师ljt12138学数学,今天他学了等差数列,ljt12138决定给他留一道练习题. 题目描述 ljt12138首先建了\(n\)个特斯拉电磁 ...

  2. Luogu P4933 大师【dp】By cellur925

    题目传送门 题目大意:给你一个序列,求子序列为等差数列的子序列个数.序列长度$n<=2000$,最高的塔高$v<=20000$. 这种计数类的题,大概就是dp的套路了⑧.开始设计的是一个错 ...

  3. E - Max Sum Plus Plus Plus HDU - 1244 (线性区间DP)

    题目大意:  值得注意的一点是题目要求的是这些子段之间的最大整数和.注意和Max Sum Plus Plus这个题目的区别. 题解: 线性区间DP,对每一段考虑取或者不取.定义状态dp[i][j]指的 ...

  4. Luogu P1541 乌龟棋 【线性dp】

    题目背景 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 题目描述 乌龟棋的棋盘是一行 N 个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第 N 格是终点,游戏要求玩家控制一个乌龟 ...

  5. 【题解】POJ1934 Trip (DP+记录方案)

    [题解]POJ1934 Trip (DP+记录方案) 题意: 传送门 刚开始我是这么设状态的(谁叫我DP没学好) \(dp(i,j)\)表示钦定选择\(i\)和\(j\)的LCS,然而你会发现这样钦定 ...

  6. 【题解】剪纸条(dp)

    [题解]剪纸条(dp) HRBUST - 1828 网上搜不到题解?那我就来写一篇吧哈哈哈 最优化问题先考虑\(dp\),设\(dp(i)\)表示将前\(i\)个字符(包括\(i\))分割成不相交的回 ...

  7. 【题解】地精部落(DP)

    [题解]地精部落(DP) 设\(f_i\)表示强制第一个是谷的合法方案数 转移枚举一个排列的最大值在哪里,就把序列分成了互不相干的两个部分,把其中\(i-1\choose j-1\)的数字分配给前面部 ...

  8. (线性结构dp )POJ 1260 Pearls

    Pearls Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 10558   Accepted: 5489 Descripti ...

  9. 【Luogu】P3239亚瑟王(概率DP)

    题目链接 请看luogu第一篇题解 #include<cstdio> #include<algorithm> #include<cstring> #include& ...

  10. LibreOJ 6177 题解(状压DP)

    题面 传送门 分析 刚看到这道题时想的是跟最短哈密顿路类似的二进制状压DP,先用floyd处理距离 但是此题用二进制不够,应该用三进制 0,1,2分别表示未送,正在送,已送完 dp[s][i]表示当前 ...

随机推荐

  1. JPEG格式研究——(4)反量化、逆ZigZag变化和IDCT变换

    反量化 反量化其实很简单,将霍夫曼解码出来的数据乘上对应的量化表就好了 通过当前色度选择出SOF中的Component,其中的Tqi指出了这一色度所需的量化表id Component的结构如下: 名称 ...

  2. 超详细 HarmonyOS 开发教程之开发环境搭建指南

    HarmonyOS开发环境搭建指南:DevEco Studio安装教程 一.系统要求 操作系统:Windows 10 64位或更高版本 RAM:至少8GB,推荐16GB 硬盘空间:至少10GB可用空间 ...

  3. uni-app Vue3项目引入Tailwind CSS

    前情 Tailwind CSS 是一个原子类 CSS 框架,它将基础的 CSS 全部拆分为原子级别,同时还补全各种浏览器模式前缀,兼容性也不错.它的工作原理是扫描所有 HTML 文件.JavaScri ...

  4. 2024-12-18:正方形中的最多点数。用go语言,给定一个二维数组 points 和一个字符串 s,其中 points[i] 表示第 i 个点的坐标,s[i] 表示第 i 个点的标签。 如果一个正

    2024-12-18:正方形中的最多点数.用go语言,给定一个二维数组 points 和一个字符串 s,其中 points[i] 表示第 i 个点的坐标,s[i] 表示第 i 个点的标签. 如果一个正 ...

  5. 08C++选择结构(2)——教学

    一.逻辑变量 教学视频 存储类似灯亮或灯灭.是男还是女等结果只有两种可能的数据时,可以使用逻辑型变量. 逻辑型变量用关键字bool定义,所以又称为布尔变量,其值只有两个false(假)和true(真) ...

  6. 中电金信多模态鉴伪技术抵御AI造假威胁

    ​ AI换脸技术,属于深度伪造最常见方式之一,是一种利用人工智能生成逼真的虚假人脸图片或视频的技术.基于深度学习算法,可以将一个人的面部特征映射到另一个人的面部,创造出看似真实的伪造内容.近年来,以A ...

  7. 强化学习算法中的log_det_jacobian

    相关: https://colab.research.google.com/github/google/brax/blob/main/notebooks/training_torch.ipynb 之前 ...

  8. spring boot 启动原理解析

    https://www.cnblogs.com/xiaoxi/p/7999885.html 我们开发任何一个Spring Boot项目,都会用到如下的启动类 1 @SpringBootApplicat ...

  9. 国产系统中标麒麟neokylin上的视频监控系统

    一.功能特点 (一)软件模块 视频监控模块,各种停靠小窗体子模块,包括设备列表.图文警情.窗口信息.云台控制.预置位.巡航设置.设备控制.悬浮地图.网页浏览等. 视频回放模块,包括本地回放.远程回放. ...

  10. CentOS 集群初始化设置

    0. 前置操作 centos-7.9.2009-isos-x86_64安装包下载_开源镜像站-阿里云 下载CentOS-7-x86_64-DVD-2009.iso即可 1. 配置静态网络 1.1 查看 ...