高维前缀和(SOSDP)速通
高维前缀和(SOSDP)速通
前言
长话短说
举例描述
给定 \(n\) 和一个集合 \(A = \{a_1, a_2, a_3, ..., a_n\}\),定义:\(SUM(X)=\sum\limits_{v \in X} v\),求 \(\sum\limits_{T \subseteq A} SUM(T)\)(子集和)
暴力 \(O(4^n)\)
设 \(f[s]\) 表示状态为 \(s\) 的 子集和
注意到,易证
for(int i = 0 ; i < (1 << n) ; i ++){
for(int j = 0 ; j < n ; j ++){
if((i & j) == j) f[i] += A[j];
}
}
暴力 \(O(3^n)\)
也易证
for(int i = 0 ; i < (i << n) ; i ++){
f[i] = A[i];
for(int j = i ; j > 0 ; j = (j - 1) & i){
f[i] += A[j];
}
}
正解 \(O(N \times 2 ^ N)\)
1
设 \(g[s][i]\) 表示状态为 \(s\) ,每次修改后 \(i+1\) 位的一种方案
递推
for(int i = 0 ; i < (1 << n) ; i ++){
g[i][0] = A[i];
for(int j = 0 ; j < n ; j ++){
if(i >> j & 1) g[i][j + 1] = g[i][j] + g[i ^ (1 << j)][j];
else g[i][j + 1] = g[i][j];
}
f[i] = g[i][n];
}
2
每一层只往下转移,所以滚一下
for(int i = 0 ; i < (1 << n) ; i ++) f[i] = A[i];
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)];
}
}
完整代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<set>
#include<map>
#include<unordered_map>
#include<queue>
#define int long long
using namespace std;
const int N = 3e6 + 10;
const int mod = 1e9 + 7;
const double eps = 1e-9;
typedef long long LL;
typedef unsigned long long ULL;
int a[N];
int A[N];
int f[N];
signed main(){
// freopen("echo.in", "r", stdin);
// freopen("echo.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i = 1 ; i <= n ; i ++) cin>>a[i];
for(int i = 0 ; i < (1 << n) ; i ++){
for(int j = 0 ; j < n ; j ++){
if(i >> j & 1){
A[i] += a[j + 1];
}
}
}
for(int i = 0 ; i < (1 << n) ; i ++) f[i] = A[i];
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)];
}
}
cout<<f[(1 << n) - 1];
return 0;
}

浙公网安备 33010602011771号