SOS DP学习笔记

先给一道题目:

  • 给定 n n n( n ≤ 20 n \le 20 n≤20)个数,用 a 1 a_1 a1 到 a n a_n an 表示,对于所有 i ∈ 0 , 2 n − 1 i\in 0,2\^n-1 i∈0,2n−1 求 f i = ∑ j ∈ i a j f_i=\sum_{j\in i}^{} a_j fi=∑j∈iaj,其中 i i i 表示一个集合。

这就是 SOS DP 的常见形式。

SOS DP 本质上就是一个高维前缀和,我们先思考一下二维前缀和是怎么求的:

cpp 复制代码
for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++) s[i][j]+=s[i-1][j];
for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++) s[i][j]+=s[i][j-1];

这是二维前缀和的其中一种写法,我们先将二维的其中一维相加,再将另一维相加。

仿造二维前缀和,我们可以写出 SOS DP:

cpp 复制代码
for(int i=0;i<n;i++)
	for(int j=0;j<(1<<n);j++) 
		if(j&(1<<i)) f[j]+=f[j^(1<<i)];

例题

COCI 2011/2012 #6 KOŠARE

题意

给定 n n n 个数,用 a 1 a_1 a1 到 a n a_n an 表示,选出若干个数使得这些数的或和为 2 m − 1 2^m-1 2m−1,问有多少种选数的方式。

n ≤ 10 6 , m ≤ 20 n\le 10^6,m\le 20 n≤106,m≤20。

思路

设 g i g_i gi 表示满足 a j ∈ i a_j\in i aj∈i 的 j j j 的个数, d p i dp_i dpi 表示有多少种选择方案使得选出的数的或和是 i i i 的子集 ,显然有 d p i = 2 f i − 1 dp_i = 2^{f_i}-1 dpi=2fi−1。

考虑容斥,答案即为 ∑ i = 0 2 m − 1 ( − 1 ) m − b i d p i \sum_{i=0}^{2^m-1} (-1)^{m-b_i} dp_i ∑i=02m−1(−1)m−bidpi,其中 b i b_i bi 表示 i i i 在二进制下 1 1 1 的个数。

有了 SOS DP,代码会非常好写。

代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5,mod=1e9+7;
int n,m,a[N];
long long g[N],dp[N],jie[N];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		int k;
		cin>>k;
		while(k--)
		{
			int x;
			cin>>x;
			a[i]+=(1<<(x-1));
		}
		g[a[i]]++;
	}
	for(int i=0;i<m;i++)
		for(int j=0;j<(1<<m);j++) 
			if(j&(1<<i)) g[j]+=g[j^(1<<i)];
	jie[0]=1;
	for(int i=1;i<=n;i++) jie[i]=2ll*jie[i-1]%mod;
	for(int i=0;i<(1<<m);i++) dp[i]=jie[g[i]]-1;
	for(int i=0;i<m;i++)
		for(int j=0;j<(1<<m);j++)
			if(j&(1<<i)) dp[j]-=dp[j^(1<<i)];
	cout<<(dp[(1<<m)-1]%mod+mod)%mod;
	return 0;	
} 
相关推荐
专注仿真19 小时前
知识问答模型用到的算法集详解及代码实例
python·算法·代码·大模型训练·控制算法
罗西的思考20 小时前
【轻量化复刻大模型 / KIMI K3】AttnRes 模块交互式演示
人工智能·算法·机器学习
小七在进步20 小时前
string的使用(1)
c++·visual studio
白杨尚青21 小时前
C++进阶篇(六):map 和 set 的模拟实现——用红黑树封装 mymap/myset(迭代器 + operator[])
开发语言·c++·迭代器·stl·set·map·红黑树
好悬给我拽开线1 天前
ZILOT 论文解析
算法
kloc1 天前
聊天室项目入口网关子服务的实现
linux·c++
饮水桶_T1 天前
JAVA IO流笔记
开发语言·数据结构·算法
柳鲲鹏1 天前
内斗学概论:43、平级内斗
笔记
星夜离尘561 天前
MATLAB 离散数据分箱:半开区间、末端点与计数、加权和、平均
算法·matlab
Pniubi1 天前
力扣76.最小覆盖子串
算法·leetcode·职场和发展