[P2396] yyy loves Maths VII

Link:

P2396 传送门

Solution:

一眼能看出$O(n*2^n)$的状压$dp$

但此题是个卡常题,$n=23/24$的时候就别想过了

这题算是提供了一种对状压$dp$的优化思路吧

原来我们要用$n$的时间来查找当前有哪些位为1,然后从这些位来转移

但实际上可以通过树状数组中$lowbit$函数的方式用$popcount(i)$的复杂度来得到所有的1

此时总的复杂度降到了$O(sum_{i=1}^{2^n-1} popcount(i))$,实际上就是$O(n*2^{n-1})$

虽然只减少了1倍的时间,但开个$O2$还是勉强能卡过去

Tip:使用这种优化时只能从$dp[i^(1<<j)]$向$dp[i]$转移,而不能从$dp[i]$向$dp[i|(1<<j)]$转移了

Code:

#include <bits/stdc++.h>

using namespace std;
const int MAXN=1<<25,MOD=1e9+7;
int n,dat[MAXN],dp[MAXN],m,m1,m2,lst,t;

void inc(int &a,int b){a=(a+b>=MOD)?(a+b-MOD):a+b;}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d",&dat[1<<i]);
    scanf("%d",&m);
    if(m>=1) scanf("%d",&m1);
    if(m>=2) scanf("%d",&m2);
    
    dp[0]=1;int MAX=(1<<n)-1;
    for(int i=1;i<=MAX;i++)
    {
        lst=i&(-i);
        dat[i]=dat[i^lst]+dat[lst];
        if(dat[i]==m1||dat[i]==m2) continue;
        
        for(t=i;t;t^=lst,lst=t&(-t))
            inc(dp[i],dp[i^lst]);
    }
    printf("%d",dp[MAX]);
    return 0;
}
原文地址:https://www.cnblogs.com/newera/p/9282103.html