高维前缀和(SOSDP)

更新日志

概念

高维前缀和的名字已经很显然了,不做过多讲解。

思路

基本形式

我们较为熟知的二维前缀和,通常情况下使用了容斥的思想。事实上,更通常的多维前缀和形式往往长下面这样:

    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            for(int k=1;k<n;k++){
                s[i][j][k]+=s[i][j][k-1];
            }
        }
    }
    for(int i=0;i<n;i++){
        for(int j=1;j<n;j++){
            for(int k=0;k<n;k++){
                s[i][j][k]+=s[i][j-1][k];
            }
        }
    }
    for(int i=1;i<n;i++){
        for(int j=0;j<n;j++){
            for(int k=0;k<n;k++){
                s[i][j][k]+=s[i-1][j][k];
            }
        }
    }

这么写是正确的,但是很繁琐。通常情况下,SOSDP 应该是不会只有这么粗暴的解决方式的。

通常形式

一般情况下,我们都可以使用状态压缩来解决多维前缀和问题。

更具体地,这一类问题往往都与位运算有关,比如要求你找出集合内与为 \(0\) 的数对。或者用一些形象化的描述,比如每个集合内都有一些物品,让你找出选一些集合使得拥有所有类型的物品的方案数,等等。

我们以第一个例子为例,详见例题1

我们可以对于每个状态找出他可选的一种与其与为 \(0\) 的数,也就是找出为 \((1<<m)-1\) 的子集的数(用语不太标准,意思是 \(a\&b=a\)\(a\)\(b\) 子集),那么就可以考虑前缀和解决这种为什么什么的子集的问题。

具体的,\(a\) 可以具象化为每一位都小于等于\(b\) 的数,这就很符合多维前缀和的形式了。

这时候,如果还使用基本形式,那码量望而生畏,事实上,有一种更简洁的写法:

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

这段代码仅适用于这道题目,但是其思路是通用的,我们可以把这么多位都压缩到同一个数中作为状态。

例题

Compatible Numbers

CF165E

用作了上面的例题,这里就不多讲解了,本身也很简单。

代码
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
template <typename Type>
using vec=vector<Type>;
template <typename Type>
using grheap=priority_queue<Type>;
template <typename Type>
using lrheap=priority_queue<Type,vector<Type>,greater<Type> >;
#define fir first
#define sec second
#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)
#define dprint(x) cout<<#x<<"="<<x<<"\n"

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7/*998244353*/;

const int N=1e6+5,M=22;

int n;
int a[N],f[1<<M];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        f[a[i]]=a[i];
    }
    for(int i=0;i<M;i++){
        for(int j=0;j<1<<M;j++){
            if((j>>i&1)&&(f[j^(1<<i)])){
                f[j]=f[j^(1<<i)];
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(f[(1<<M)-1^a[i]])cout<<f[(1<<M)-1^a[i]];
        else cout<<-1;
        cout<<" ";
    }
    return 0;
}

KOŠARE

LG6442

个人语言表述不够清晰,附上一份个人认为讲的不错的博客:

链接

代码
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef __int128 i128;
typedef double db;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,ll> pil;
typedef pair<ll,int> pli;
template <typename Type>
using vec=vector<Type>;
template <typename Type>
using grheap=priority_queue<Type>;
template <typename Type>
using lrheap=priority_queue<Type,vector<Type>,greater<Type> >;
#define fir first
#define sec second
#define pub push_back
#define pob pop_back
#define puf push_front
#define pof pop_front
#define chmax(a,b) a=max(a,b)
#define chmin(a,b) a=min(a,b)
#define dprint(x) cout<<#x<<"="<<x<<"\n"

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int mod=1e9+7/*998244353*/;

const int N=1e6+5,M=20;

int n,m;
ll s[1<<M];
int st[N];
ll ans;

ll qpow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m;
    int j,k;
    for(int i=1;i<=n;i++){
        cin>>k;
        st[i]=0;
        while(k--){
            cin>>j;
            st[i]|=1<<j-1;
        }
        s[st[i]]++;
    }
    for(int i=0;i<m;i++){
        for(int j=0;j<1<<m;j++){
            if(j&1<<i)s[j]=(s[j]+s[j^1<<i])%mod;
        }
    }
    for(int t=0;t<1<<m;t++){
        s[t]=(qpow(2,s[t])-1+mod)%mod;
        int pd=__builtin_popcount(t);
        if((pd&1)==(m&1))ans=(ans+s[t])%mod;
        else ans=(ans-s[t]+mod)%mod;
    }
    cout<<ans;
    return 0;
}
posted @ 2024-11-16 10:02  HarlemBlog  阅读(2)  评论(0编辑  收藏  举报