首页 > 其他分享 >高维前缀和SOSDP

高维前缀和SOSDP

时间:2024-11-16 09:57:03浏览次数:1  
标签:typedef 前缀 int long SOSDP pair using 高维 define

更新日志

概念

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

思路

基本形式

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

    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;
}

标签:typedef,前缀,int,long,SOSDP,pair,using,高维,define
From: https://www.cnblogs.com/HarlemBlog/p/18549045

相关文章

  • PKUSC2018 最大前缀和
    题意给一个长度为\(n\)的整数序列,求其\(n!\)种排列方式的最大前缀和(不能为空)的总和。\(n\leq20\)解法设全集为\(U\),考虑枚举作为最大前缀和的子集\(S\)。那么要求的就是\(S\)排列后严格最大前缀和在最后一个元素取到和方案数和\(U\backslashS\)排列后每个前缀......
  • 【菜笔cf刷题日常-1600】C. Good Subarrays(思维,前缀和)
    链接:Problem-1398C-Codeforces思路:考虑每一个新加入的数对于原有序列(长度、数的总和)需求的变化:如1的加入对于原有序列需求无变化;2 的加入需要原有序列长度增加1;0 的加入需要原有序列数的总和增加1;……因此,将每个数减1(如1变为0,0变为 -1)来代表这个数的......
  • 前缀素数个数的一点想法
    前缀素数个数的一点想法ideafrompp_orange,08/11/24首先对于狄利克雷卷积,我们有一种视角是设\(f(x)=\sum\limits_{i=1}^{\inf}a_{i}x^{\lni}\),这样我们直接多项式卷积就可以干狄利克雷卷积干的事情,而且方便让多项式的性质和处理手段直接嫁接到狄利克雷卷积上来。我们......
  • 鲜花:bitset求解高维偏序
    书接上回一维偏序直接做、二维偏序套线段树或归并排序、三维偏序可以树套树或者CDQ套树,那四维偏序呢?可以CDQ套树套树。那五维偏序呢?可以发现,无论是CDQ分治还是树,都很难再继续嵌套,再写下去不但码量巨大,还巨难调,效率还相当低。树或CDQ嵌套\(m\)维偏序时间复杂度为\(O(n......
  • 子集枚举优化与高维前缀和
    前缀和与二维前缀和考虑一个序列\(a\),我们如何快速求出区间\([l,r]\)的元素和呢?这很简单,我们只需求出它的前缀和序列\(\mathrm{sum}(i)=\sum_{k=1}^ia_i\),那么答案即为\(\mathrm{sum}(r)-\mathrm{sum}(l-1)\)。而对于序列\(\mathrm{sum}\),有\(\mathrm{sum}(i)=......
  • 高维前缀和
    高维前缀和二维前缀和一般的做法是容斥:for(inti=1;i<=n;++i)for(intj=1;j<=n;++j)sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];实际上也可以固定其他维度,依次在每一维上求前缀和,即:for(inti=1;i<=n;+......
  • MySQL 字符串索引和前缀索引
    前缀索引创建前缀索引altertabletaddindexidx_email(email);altertabletaddindexidx_email(email(6));使用前缀索引,定义好长度,可以做到即节省空间,又不用额外增加太多查询成本。区分度建立索引时,区分度(不重复的值)越高越好。selectcount(distanceemail)fromt......
  • ACWING 503. 借教室 二分+前缀和
    题目描述输入格式输出格式数据范围输入样例:432543213324424输出样例:-12题目分析 每个订单都是对第s到t这个区间进行操作,所以可以用差分和前缀来实现对这段区间的快速修改。在1-m个订单中一定会有最后一个能被处理的订单,因此可将1-m分为两个区......
  • 最长公共前缀
    最长公共前缀题目链接:牛客描述给你一个大小为n的字符串数组strs,其中包含n个字符串,编写一个函数来查找字符串数组中的最长公共前缀,返回这个公共前缀。示例输入:["abca","abc","abca","abc","abcc"]返回值:"abc"思路step1:确定第i个与第i+1个字符串子串相同的公共......
  • 二维前缀和模板
    二维前缀和模板题目描述:输入一个n行m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。对于每个询问输出子矩阵中所有数的和。输入格式:第一行包含三个整数n,m,q接下来n行,每行包含m个整数,表示整数矩阵。接下来......