zoukankan      html  css  js  c++  java
  • 【DP】解析 SOSdp(子集和 dp)

    引入

    (f[st]=sum_{isubseteq st} w[i]) (~) (~) ((1))

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

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

    举例来说:
    (1010_{2}) 的所有子集为 (1010_{2},1000_{2},0010_{2},0000_{2})

    那么对于 ((1)) 式,当 (st=1010_{2}) 时,(f[1010_{2}]=w[1010_{2}]+w[1000_{2}]+w[0010_{2}]+w[0000_{2}])
    子集和dp 就是用来高效求解上述的 (f) 的。

    原理

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

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

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

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

    (dp(10110_{2},2)=w[10100_{2}]+w[10010_{2}]+w[10000_{2}]+w[10110_{2}])
    (这里就是 (index in [0,2]) 部分的所有子集)

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

    • (i) 位为 (0) 时,有 (dp(st,i)=dp(st,i-1))
    • (i) 位为 (1) 时,有 (dp(st,i)=dp(st,i-1)+dp(st oplus 2^i,i-1))
      原理很好理解,当 (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) 定理,转化为求 (a_{j}& a_{i}=a{i}) 的对数,用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;
    }
    
  • 相关阅读:
    ICS SIP Call移植
    ubuntu常用软件安装
    ubuntu开机自动设置屏幕亮度
    书摘《苹果是方的》
    dbml 添加时自动生成 Guid & DataTime
    English 中有趣的a和d
    asp.net 生成ul控件
    lambda c# 3.0
    0809 END Lakers
    linq c# 3.0
  • 原文地址:https://www.cnblogs.com/Tenshi/p/14520614.html
Copyright © 2011-2022 走看看