计蒜客 15971 腾讯消消乐 题解

来源:互联网 发布:广州11选五遗漏数据 编辑:程序博客网 时间:2024/05/17 01:18

题意

腾讯推出了一款益智类游戏——消消乐。游戏一开始,给定一个长度为 n 的序列,其中第 i 个数为 A​i。
游戏的目标是把这些数全都删去,每次删除的操作为:选取一段连续的区间,不妨记为 [L,R],如果这一段区间内所有数的最大公约数≥k(k 值在游戏的一开始会给定),那么这一段区间就能被直接删去。
注意:一次删除以后,剩下的数会合并成为一个连续区间。
定义 f(i) 为进行 i 次操作将整个序列删完的方案数。
你需要实现一个程序,计算 ni=1(f(i)i) mod 1000000007

思路

状态压缩dp,对于每一个状态,记录到达这一状态需要i次操作的方案数,然后转移至下一状态,最后得到结果

代码

#include <cstdio>#include <vector>#include <algorithm>using namespace std;#define mod 1000000007long long a[20];long long ans[(1<<18)][20];long long now[20];long long anss;long long n,K,e,ii,temp,g;long long gcd(long long a,long long b){    if(a%b==0)        return b;    else if(b%a==0)        return a;    else if(a>b)        return gcd(b,a%b);    else return gcd(a,b%a);}int main(){    scanf("%lld%lld",&n,&K);    for(long long i=0;i<n;i++)        scanf("%lld",&a[i]);    e=(1LL)<<n;    ans[0][0]=1;    for(long long i=0;i<e;i++)    {        ii=i;        for(long long j=0;j<n;j++)        {            now[n-1-j]=ii%2;            ii>>=1;        }        for(long long j=0;j<n;j++)        {            if(now[j])                continue;            temp=0;            g=a[j];            for(long long k=j;k<n;k++)            {                if(now[k])                    continue;                g=gcd(g,a[k]);                if(g>=K)                {                    temp|=(1LL<<(n-1-k));                    for(long long p=1;p<=n;p++)                        ans[i|temp][p]+=ans[i][p-1];                }                else break;            }        }    }    anss=0;    for(long long i=1;i<=n;i++)        anss=(anss+((ans[e-1][i]%mod)*i)%mod)%mod;    printf("%lld\n",anss);    return 0;}