【DP】解析 SOSdp(子集和 dp)

引入

f[st]=istw[i]     (1)

解释: istst&i=i ,熟悉位运算的同学很容易看出 i 就是二进制表示的集合 stst子集

其中 w 是子集 i 所对应的贡献。

举例来说:
10102 的所有子集为 10102,10002,00102,00002

那么对于 (1) 式,当 st=10102 时,f[10102]=w[10102]+w[10002]+w[00102]+w[00002]
子集和dp 就是用来高效求解上述的 f 的。

原理

我们用 dp(st,i) 表示二进制表示的集合 st 的最后 i 位变化的所有子集的贡献的和。

听起来不太好理解,举例来说:

我们约定,一个二进制数从右到左的下标 index 分别为 0,1,2... ,如

index: 4 3 2 1 0
number:1 0 1 1 0

dp(101102,2)=w[101002]+w[100102]+w[100002]+w[101102]
(这里就是 index[0,2] 部分的所有子集)

考虑状态转移:
对于一个状态 st

  • i 位为 0 时,有 dp(st,i)=dp(st,i1)
  • i 位为 1 时,有 dp(st,i)=dp(st,i1)+dp(st2i,i1)
    原理很好理解,当 i 位为 0 时,只能选择不取。 当 i 位为 1 时可以选择取和不取,那么贡献就是取和不取的贡献之和。

代码:类似于 01 背包,我们可以去掉一维(由柿子特征恒等变形)

void sos(){
	for(int i=0;i<(1<<N);i++)
		f[i]=w[i];
	for(int i=0;i<N;i++)
		for(int st=0;st<(1<<N);st++)
			if(st&(1<<i)) f[st]+=f[st^(1<<i)];
}

例题:
https://codeforces.com/gym/102576/problem/B

分析

利用 Lucas 定理,转化为求 aj&ai=ai 的对数,用sos求解即可。

代码
#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

inline int read()
{
	char c=getchar();
	int x=0,f=1;
	while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}

const int N=20;
int w[1<<N],f[1<<N];
int n;

void sos(){
	for(int i=0;i<N;i++)
		for(int st=0;st<(1<<N);st++)
			if(st&(1<<i)) f[st]+=f[st^(1<<i)];
}

int main(){
	int T; cin>>T;
	while(T--){
		memset(f,0,sizeof f);
		n=read();
		for(int i=1;i<=n;i++){
			w[i]=read();
			f[w[i]]++;
		}
		
		sos();
		
		ll res=0;
		for(int i=1;i<=n;i++)
			res+=f[w[i]];
		cout<<res<<endl;
	}	
	return 0;
}
posted @   HinanawiTenshi  阅读(1466)  评论(3编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示