高维前缀和(sos dp)
来看以下例题:令
好的,有没有什么更优秀的算法呢?引入高维前缀和。就比如我们要求一个三维的前缀和,需要写一个比较长的容斥式,并且扩展到多维转移的时间复杂度为
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
for(int k = 1; k <= n; k ++) pre[i][j][k] += pre[i - 1][j][k];
}
}
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
for(int k = 1; k <= n; k ++) pre[i][j][k] += pre[i][j - 1][k];
}
}
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
for(int k = 1; k <= n; k ++) pre[i][j][k] += pre[i][j][k - 1];
}
}
这个看一看应该都能懂吧。(
好的,回到这道题,直接给出递推式。
for(int i = 0; i < n; i ++) {
for(int j = 0; j < (1 << n); j ++) {
if((j >> i) & 1) dp[j] += dp[j ^ (1 << i)];
}
}
乍一看就是一个状压,但枚举
并且,这里可以扩展,不仅仅是
CF165E Compatible Numbers
啊,其实不难。令全集为
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <climits>
#include <cstring>
using namespace std;
const int MAXN = 1e6 + 5, MAXM = (1 << 22) + 5;
int n, a[MAXN], dp[MAXM], t, q;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]), dp[a[i]] = a[i], t = max(t, a[i]); t = log(t) / log(2) + 1;
for(int i = 0; i < t; i ++) for(int j = 0; j < (1 << t); j ++) if((j >> i) & 1) if(dp[j ^ (1 << i)]) dp[j] = dp[j ^ (1 << i)];
for(int i = 1; i <= n; i ++) q = ((1 << t) - 1) ^ a[i], printf("%d ", dp[q] ? dp[q] : -1);
return 0;
}
CF1208F
注意 sos dp 是不能动态做(即动态插入数)的。(废话)
发现后面那个
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <climits>
#include <cstring>
using namespace std;
const int MAXN = 1e6 + 5, MAXM = (1 << 21) + 5;
int n, a[MAXN], t, dp[MAXM][2], ans;
int Max(int x, int y) { return x > y ? x : y; }
void Insert(int S, int val) {
if(val > dp[S][0]) dp[S][1] = dp[S][0], dp[S][0] = val;
else dp[S][1] = Max(dp[S][1], val);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]), Insert(a[i], i), t = max(t, a[i]);
t = log(t) / log(2) + 1;
for(int i = 0; i < t; i ++) {
for(int j = 0; j < (1 << t); j ++) {
if(!((j >> i) & 1)) Insert(j, dp[j ^ (1 << i)][0]), Insert(j, dp[j ^ (1 << i)][1]);
}
}
for(int i = 1; i <= n; i ++) {
int res = 0;
for(int j = t - 1; j >= 0; j --) {
if((a[i] >> j) & 1) continue;
if(dp[res | (1 << j)][1] > i) res |= (1 << j);
}
if(res || dp[res][1] > i) ans = Max(ans, res | a[i]);
}
printf("%d", ans);
return 0;
}
未完待续(咕咕咕~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】