騰訊消消樂 狀壓dp
題目鏈接
題意是就點進去題目鏈說的那樣.
看一下數據由於n<18 然後考慮用狀壓dp
dp[i][s] 表示消去i次得到的狀態爲s的方案數,1代表刪了0代表還沒刪,當我們枚舉到一個狀態s時,取出所有0位,然後枚舉其中的一段,刪除掉,即可轉移到下一步的該狀態,
例如1010101,即表示當前序列已經被刪剩下3個,然後枚舉這3個數字中的一段如
110,則當前狀態可以轉移到下一步的1010101+0101000狀態.然後是否能轉移則可以預處理出每一個狀態的GCD.
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define LONG long long
const LONG INF=0x3f3f3f3f;
const LONG MOD=1e9+ 7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson l , mid , rt<< 1
#define rson mid + 1 ,r , (rt<<1)+1
#define root 1, m , 1
LONG dp[20][(1<<18) + 20] ;
int num[20] ;
int take[(1<<18) + 30] ;
int gcd(int a , int b)
{
int c = a%b ;
while(c) a = b ,b = c , c = a %b ;
return b;
}
int main()
{
int n ;
int K ;
while(cin >> n>>K)
{
for(int i = 1 ; i <= n ; ++i)
scanf("%d",&num[i]) ;
clr0(dp) ;
int bit[20 ] ;
dp[0][0] = 1;
for(int i = 1 ; i< (1<< n) ; ++ i)
{
int tmp = i ;
int p = 0 ;
int t = 0 ;
clr0(bit) ;
for(int j = 0 ;j < n ; ++ j)
if( ( tmp>>j )& 1) bit[++p] = j+1 ;
int judge = num[bit[1]];
for(int j =1; j < p ;++j)
judge = gcd (judge, num[ bit[j+1] ] ) ;
take[i] = judge ;
}
for(int i = 0 ; i < n ; ++i)
for(int j = 0; j < (1<< n) ; ++j)
{
int tmp = j ;
clr0(bit) ;
int p= 0;
for(int k = 0 ; k < n ; ++ k)
if( ( tmp >> k & 1) == 0) bit[++ p] = k ;
for(int k = 1 ;k <= p ; ++ k)
{
int now = j ;
int Del = 0 ;
for(int l = k ;l <= p ; ++l )
{
now += (1<<bit[l]) ;
Del += (1<<bit[l]) ;
if(take[Del] >= K )
{
dp[1+i][ now ] += dp[i][j] ;dp[i+1][now] %= MOD ;
}
}
}
}
LONG ans = 0;
for(int i = 1; i<= n ; ++i)
ans = (ans + dp[i][(1<<n)-1] *i ) % MOD ;
// printf("%d %lld\n",i, dp[i][(1<<n)- 1]) ;
cout<<ans<<endl;
}
}