【PR #1 A】删数(DP)

删数

题目链接:PR #1 A

题目大意

给你一个数组,每次你可以选择一个满足条件的位置(条件是他旁边两个数的平均数是它,边界不可能满足),把它删去。
然后问你这个数组在若干次操作之后最短可以有多段。

思路

首先看到这个条件自然就是差分一下。
然后就会发现操作变成了把差分数组两个相邻相同的数合并得到它们的和。

然后不难看出一个数合并不会超过 log 值域次。(除了 0,这个特判)
然后你会发现你可以把数分类,不同类之间是绝对不可能合并到的。
然后同一类的就是正负相同而且把低位的 0 去掉(方法是 /lowbit(x))是一样的。

那我们就可以把差分数组按这个分段,每段都是一样的,然后逐个解决。
首先特判 0,那最后就合剩一个 0,所以留下一个。
然后别的我们考虑 DP,设 fi 为搞前 i 个的答案,然后我们考虑每次直接放入合并的一块转移。
那我们就要预处理出 gi,ji 为右端点搞出 2j 大小的话左端点在哪里(如果没有就是 0
然后转移即可。

然后最后还原乘原数组,长度 +1,所以输出答案加一。

代码

#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N = 3e5 + 100; int n, a[N], ans, f[N], g[N][41]; int ops(int x) { if (!x) return 0; return x / (x & -x); } int get_ji(int x) { int re = 0; while (!(x & 1)) re++, x >>= 1; return re; } int clac(int l, int r) { for (int i = l; i <= r; i++) g[i][get_ji(a[i] & -a[i])] = i; for (int i = 1; i <= 40; i++) for (int j = l; j <= r; j++) { if (g[j][i - 1] > l && g[g[j][i - 1] - 1][i - 1] >= l) { g[j][i] = g[g[j][i - 1] - 1][i - 1]; } } f[l - 1] = 0; for (int i = l; i <= r; i++) { f[i] = 2e9; for (int j = 0; j <= 40; j++) if (g[i][j] >= l) f[i] = min(f[i], f[g[i][j] - 1] + 1); } return f[r]; } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i < n; i++) a[i] = a[i + 1] - a[i]; int ans = 0; for (int L = 1, R; L < n; L = R + 1) { R = L; while (R < n - 1 && ops(a[R]) == ops(a[R + 1])) R++; if (ops(a[R])) ans += clac(L, R); else ans += 1; } printf("%d\n", ans + 1); for (int i = 1; i < n; i++) for (int j = 0; j <= 40; j++) g[i][j] = 0; } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/16121172.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(106)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示