首页 > 编程知识 正文

高维前缀和总结前缀区别,高维度的定义

时间:2023-05-05 11:15:01 阅读:244431 作者:287

前言

今天中午不知怎么的对这个东西产生了兴趣,感觉很神奇,结果花了一个中午多的时间来看QAQ
下面说下自己的理解。

高维前缀和一般解决这类问题:

对于所有的(i,0leq ileq 2^n-1),求解(sum_{jsubset i}a_j)。

显然,这类问题可以直接枚举子集求解,但复杂度为(O(3^n))。如果我们施展高维前缀和的话,复杂度可以到(O(ncdot 2^n))。

说起来很高级,其实代码就三行:

for(int j = 0; j < n; j++) for(int i = 0; i < 1 << n; i++) if(i >> j & 1) f[i] += f[i ^ (1 << j)];

相信大家一开始学的时候就感觉很神奇,这是什么东西,这跟前缀和有什么关系?
好吧,其实看到后面就知道了。

正文

二维前缀和

一维前缀和就不说了,一般我们求二维前缀和时是直接容斥来求的:
[ sum_{i,j}=sum_{i-1,j}+sum_{i,j-1}-sum_{i-1,j-1}+a_{i,j} ]
但还有一种求法,就是一维一维来求,也可以得到二维前缀和:

for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) a[i][j] += a[i - 1][j];for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) a[i][j] += a[i][j - 1];

模拟一下就很清晰了。

三维前缀和

同二位前缀和,我们也可以对每一维分别来求:

for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) for(int k = 1; k <= n; k++) a[i][j][k] += a[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++) a[i][j][k] += a[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++) a[i][j][k] += a[i][j][k - 1];

高维前缀和

接下来就步入正题啦。
求解高维前缀和的核心思想也就是一维一维来处理,可以类比二维前缀和的求法稍微模拟一下。
具体来说代码中的(f[i] = f[i] + f[i xor (1 << j)]),因为我们是正序枚举,所以(i xor (1 << j))在当前层,而(i)还在上层,所以我们将两个合并一下就能求出当前层的前缀和了QAQ。
然后...就完了,好像没什么好说的。

应用 子集

那这跟子集有啥关系?在二进制表示中,发现当(isubset j)时,其实这存在一个偏序关系,对于每一位都是这样。而我们求出的前缀和就是满足这个偏序关系的。
回到开始那个问题,初始化(f[i]=a_i),直接求高维前缀和,那么最终得到的(f)就是答案数组了。

超集

理解了子集过后,我们将二进制中的每一个(1)当作(0)对待,(0)当作(1)对待求出来的就是超集了~相当于从另一个角出发来求前缀和。
求超集代码如下:

for(int j = 0; j < n; j++) for(int i = 0; i < 1 << n; i++) if(!(i >> j & 1)) f[i] += f[i ^ (1 << j)];

似乎(FMT)(快速莫比乌斯变换)就是借助高维前缀和这个东西来实现的。
虽然只有三行代码,但很神奇QAQ

例题

arc 100E

题意:
给出(2^n)个数:(a_0,a_1,cdots,a_{2^n-1})。
之后对于(1leq kleq 2^n-1),求出:(a_i+a_j)的最大值,同时(i or jleq k)。

思路:
挺奇妙的一个题,需要将问题转换。

发现我们可以对每个(k),求出最大的(a_i+a_j)并且满足(i or j=k),最后答案就为一个前缀最大值。但这种形式也不好处理,我们可以将问题进一步转化为(i or jsubset k)。那么我们就将问题转化为了子集问题。所以接下来就对于每个(k),求出其所有子集的最大值和次大值就行了。直接枚举子集复杂度显然不能忍受,其实直接上高位前缀和搞一下就行~

注意一下细节,集合中一开始有一个数。
代码如下:

#include <bits/stdc++.h>#define MP make_pair#define fi first#define se second#define sz(x) (int)(x).size()#define INF 0x3f3f3f3f3f//#define Localusing namespace std;typedef long long ll;typedef pair<int, int> pii;const int N = 20;int n;pii a[1 << N];pii merge(pii A, pii B) { if(A.fi < B.fi) swap(A, B); pii ans = A; if(B.fi > ans.se) ans.se = B.fi; return ans;}void run() { for(int i = 0; i < 1 << n; i++) { int x; cin >> x; a[i] = MP(x, -INF); } for(int j = 0; j < n; j++) { for(int i = 0; i < 1 << n; i++) { if(i >> j & 1) a[i] = merge(a[i], a[i ^ (1 << j)]); } } int ans = 0; for(int i = 1; i < 1 << n; i++) { ans = max(ans, a[i].fi + a[i].se); cout << ans << 'n'; }}int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20);#ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout);#endif while(cin >> n) run(); return 0;}

转载于:https://www.cnblogs.com/heyuhhh/p/11585358.html

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。