首页 > 其他分享 >Trick 8:子集卷只因的 SOSDP 做法

Trick 8:子集卷只因的 SOSDP 做法

时间:2023-01-12 22:58:00浏览次数:41  
标签:SOSDP text ll 1048576 Trick 子集 popcnt sum

问题引入:对于两个数组 \(a\),\(b\),长度为 \(2^n\),从 \(0\) 开始标号。

求 \(c_i = \sum\limits_{p \& q = i} a_pb_q\)。

关于这个问题的求解,我们可以使用 SOSDP 完成一系列 无重计数 以及 容斥 的操作。

首先,我们考虑这个会重的计数:\(c_i = \sum\limits_{i \subseteq p,q} a_pb_q\)

这个其实只要计算 \(a'_i = \sum \limits_{k \subseteq i} a_k\) 然后直接每一位乘起来即可。

然后我们发现,去掉重复的也可以用 SOSDP。那么做完了。\(\mathcal{O}(n2^n)\)

这个问题是解决了,考虑这样一个更实用的问题:如果要求满足 \(\text{popcnt}(p) + \text{popcnt}(q) = \text{popcnt}(i), p | q = i\),还能套用刚才的吗?

可以!加一维度表示维护的 \(\text{popcnt}\)。这个 trick 在我写那个啥生成树计数的时候本来可以用上的,但是复杂度寄了点()这里用上了。时间复杂度 \(\mathcal{O}(n^22^n)\)。


void add(ll &a,ll b){
	a += b;
	if(a>=mod) a-=mod;
}
ll n, a[21][1048576], b[21][1048576], c[21][1048576];
void solve() {
    n = read();
    for (ll i = 0; i < (1 << n); i++) a[__builtin_popcount(i)][i] = read();
    for (ll i = 0; i < (1 << n); i++) b[__builtin_popcount(i)][i] = read();
    for(ll k = 0; k <= n; k ++)
    for (ll i = 0; i < n; i++)
        for (ll j = 0; j < (1 << n); j++) {
            if (j & (1 << i)) {
                add(a[k][j],a[k][j ^ (1 << i)]);
        	}
        }
    for(ll k = 0; k <= n; k ++)
    for (ll i = 0; i < n; i++)
        for (ll j = 0; j < (1 << n); j++) {
            if (j & (1 << i)) {
                add(b[k][j],b[k][j ^ (1 << i)]);
        	}
        }
		for(ll k = 0; k <= n; k ++)
			for(ll l = 0; k+l <= n; l ++)
				for (ll i = 0; i < (1 << n); i++) add(c[k+l][i] , 1ll * a[k][i] * b[l][i] % mod);
				// 注意,k,l 放在循环前面,可以保证数组下标访问的连续性以减少常数。
	for(ll k = 0; k <= n; k ++)
    for (ll i = 0; i < n; i++)
        for (ll j = (1 << n) - 1; j >= 0 ; j --) {
            if (j & (1 << i)) {
                add(c[k][j] ,(mod - c[k][j - (1 << i)]));
            }
        }
    for (ll i = 0; i < (1 << n); i++) printf("%d ", c[__builtin_popcount(i)][i]);
}

标签:SOSDP,text,ll,1048576,Trick,子集,popcnt,sum
From: https://www.cnblogs.com/BreakPlus/p/17048178.html

相关文章

  • 90. 子集 II
    90.子集II难度中等997收藏分享切换为英文接收动态反馈给你一个整数数组nums,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。返......
  • 78. 子集
    78.子集难度中等1890收藏分享切换为英文接收动态反馈给你一个整数数组nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。你可......
  • numpy_tricks
    NumpyTricks这篇文章不定期更新,主要是记录在使用numpy过程中一些有效的tricks(或者重要的API)importnumpyasnpnumpy.where()numpy.where(condition,[,x,y])参......
  • 高维前缀和与 SOSDP
    高维前缀和高维前缀和,就是对每一个高维空间的点\((a_1,a_2,\cdots,a_k)\),求\(\displaystyle\sum_{b_1=0}^{a_1}\sum_{b_2=0}^{a_2}\cdots\sum_{b_k=0}^{a_k}val_{(......
  • 【LeeCode】416. 分割等和子集 -- todo
    【题目描述】给你一个 只包含正整数 的 非空 数组 ​​nums​​ 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。​​​​https://leetcode.c......
  • 莫队trick
    不带修(belong[a.l]^belong[b.l])?(belong[a.l]<belong[b.l]):((belong[a.l]&1)?a.r<b.r:a.r>b.r)待修(belong[a.l]^belong[b.l])?belong[a.l]<......
  • Trick 6: 组合数学小技巧
    求解递推式\(a_n=xa_{n-1}+y\)。分析:换元,加入一个常数\(c\),我们期望得到这样一个结果:\(a_n+c=x(a_{n-1}+c)\)。化简后和上式对应,解得\(c=\dfrac{y}{x-......
  • Trick 5: 关于 GCD 的一些处理方法和性质
    经典的mobius:\(\varepsilon(x)=\sum\limits_{d|x}\mu(d)\)经典的euler:\(x=\sum\limits_{d|x}\varphi(d)\)处理区间问题。如果考虑一段区间的\(\gcd\),那......
  • 三化二叉树trick
    三选一化二叉套路概述这个套路是针对某一建模题的。三选一其实可以扩展到N选一,模型具体如下。发现某种状态可以扩展出\(N\)个状态,且有一个状态相较而言比较特殊(如其他......
  • 698. 划分为k个相等的子集
    题目描述给出一个数组nums,问能不能分为k份,每份的和都相等,数组的长度不超过16f1-状态压缩+记忆化搜索基本分析数组长度不超过16?暗示可以对选择情况用二进制进行表达df......