腾讯推出了一款益智类游戏——消消乐。游戏一开始,给定一个长度为 n 的序列,其中第 i 个数为 Ai。
游戏的目标是把这些数全都删去,每次删除的操作为:选取一段连续的区间,不妨记为 [L,R],如果这一段区间内所有数的最大公约数 ≥k(k 值在游戏的一开始会给定),那么这一段区间就能被直接删去。
注意:一次删除以后,剩下的数会合并成为一个连续区间。
定义 f(i) 为进行 i 次操作将整个序列删完的方案数。
你需要实现一个程序,计算 ∑i=1n(f(i)∗i) mod 1000000007。
输入格式
第一行输入两个整数 n,k(1≤n≤18)。
第二行输入 n 个正整数 ai(1≤ai≤105),表示初始序列中的每个数。
输入数据保证 1≤k≤min(a1,a2,…an)。
输出格式
输出一个整数,表示算出的答案。
样例说明
对于样例 1 而言,f(1)=1,f(2)=9,f(3)=26,f(4)=24。
对于样例 2,f(1)=0,f(2)=2。
样例输入1
4 1 1 1 1 1
样例输出1
193
样例输入2
2 2 2 3
样例输出2
4
样例输入3
1 233 233
样例输出3
1
先暴力枚举并打表记录所有的删除方法的可能性,然后用个最简单的dp转移一下就好。
代码:
#include<bits/stdc++.h>
#define mod 1000000007
#define ll long long
#define MAXN (1<<18)
using namespace std;
typedef pair<int,int>P;
int n,m;
int a[19];
int f[19][MAXN];//f[i][j]表示删i次且当前状态为j(压缩后的状态)
vector<int>ans[MAXN];
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
void init()
{
int t,tmp;
for(int i=0;i<(1<<n);i++)//枚举所有可能的集合状态
{
for(int j=0;j<n;j++)
{
if((i&(1<<j))==0) continue;
t=a[j],tmp=(1<<j);
ans[i].push_back(i-tmp);
for(int k=j+1;k<n;k++)
{
if((i&(1<<k))==0) continue;
t=gcd(t,a[k]);
tmp+=(1<<k);
if(t<m) break;
ans[i].push_back(i-tmp);//由i状态转移到i-tmp状态
}
}
}
}
void solve()
{
init();
f[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<(1<<n);j++)
{
for(int k=0;k<ans[j].size();k++)
{
f[i][j]+=f[i-1][ans[j][k]];//删i次的情况是由删i-1次的情况转移来的
f[i][j]%=mod;
}
}
}
ll res=0;
for(ll i=1;i<=n;i++)
res=(res+(i*f[i][(1<<n)-1])%mod)%mod;
cout<<res%mod;
}
int main()
{
//cout<<gcd(12,3);
//cout<<(1<<18);
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
solve();
}