【bzoj4762】【JZOJ5151】最小集合 題解
轉自 AwD! 的部落格
題目大意
定義一個非空集合是合法的,當且僅當它滿足以下兩個條件。
1、集合內所有元素 and 和為 0
2、它的非空子集中僅有它本身滿足 1
給出一個集合 S,求它的合法非空子集數。
(這裡的集合都是可重的)
n<=1000, 0<=a[i]<1024
題解
一個 AND 和為
也就是說有
如果令
那麼答案就是
這玩意是很難直接 dp 的,因此容斥這些限制
也就是說要去求補集:求某些限制同時不合法的方案數
於是有這麼一個式子:
注意到
也就是如果某些限制同時不滿足的話,那麼如果把所有對應集合的AND和 按位或起來的和為
因此答案就等於
暴力的話列舉集合
如果要 DP 的話只需要記錄
令
若第
如果不選第
如果
如果
初始狀態為
最終答案
程式碼
//from rzO_KQP_Orz
#include<cstdio>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
const int maxn=1005, maxa=1030;
const LL mo=1e9+7;
int n,a[maxn];
LL f[2][maxa][maxa]; //先k後j可以定址優化
int main()
{
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]);
f[0][1023][1023]=1;
int p=0;
fo(i,0,n-1)
{
fo(k,0,1023)
{
for(int j=k; j; j=(j-1)&k) f[p^1][k][j]=0;
f[p^1][k][0]=0;
}
fo(k,0,1023)
{
for(int j=k; j; j=(j-1)&k)
{
(f[p^1][k][j]+=f[p][k][j])%=mo;
(f[p^1][k&a[i+1]][j&a[i+1]]+=f[p][k][j])%=mo;
f[p^1][j|k&a[i+1]][j&a[i+1]]-=f[p][k][j];
}
(f[p^1][k][0]+=f[p][k][0])%=mo;
}
p^=1;
}
printf("%lld\n",(f[p][0][0]+mo)%mo);
}