题意:
给定n,m和c[i],要求构造一串序列,a[i],要保证,a[i]小于2的m次方,大于0,,a[i]不能整除以c[i],a[i]&a[i+1]==0,求这种序列的方案个数,取模于1e9,n<50,m<15
思路:
SOS DP!(高维前缀和DP)//在开始前我们应该学到了到,高维前缀和是针对做的事情是 i&j==i(也就是i是j的子集)有多少种情况
//那么这里,我们发现题面要满足a[i]&a[i+1]=0,则f[i]是从上一次结果中
//所有满足i&j=0的地方转移过来的i&j=0即i&(~j)=i,即i为~j的子集,
//那么我们每次对上一次的结果进行下标取反操作,
//那么求当前f[i],就是求出以i为子集的上一次计算出的f值的高维前缀和。
// 对于c[i]这个条件,我们每轮计算后将c[i]倍数为下标的dp值置0即可。
Code:
int f[1<<15] ;
int t,n,m,c[52];
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>c[i];
for(int i=0;i<(1<<m);i++)
f[i]=(i%c[1]!=0);
for(int i=2;i<=n;i++){
for(int j=0;j<m;j++)
for(int k=0;k<(1<<m);k++)
if((1<<j)&k)f[k]=(f[k]+f[k-(1<<j)])%mod;//求了j是k的子集
for(int j=0;j<(1<<m-1);j++)
swap(f[j],f[(1<<m)-1-j]);//对上一次的结果进行下标取反操作
for(int j=0;j<(1<<m);j++)//将c[i]倍数为下标的dp值置0
if(j%c[i]==0)f[j]=0;
}
int ans=0;
for(int i=0;i<(1<<m);i++)
ans=(ans+f[i])%mod;//f是压过一维的,如果是二维的应该是f[n][j]
//表示选到第n个数,第n个数方案为j的方案数,把他们累加起来;
//其实等效于理解就是从子集一步步给到超集,最后统计所有最后集合里的答案有多少
cout<<ans<<endl;
}