[P2396] yyy loves Maths VII
Link:
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; }