bzoj 2734 [HNOI2012]集合选数 状压DP+预处理

这道题很神啊……

神爆了……

思路大家应该看别的博客已经知道了,但大部分用的插头DP。我加了预处理,没用插头DP,一行一行来,速度还挺快。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 100100
#define M 50
#define yu 1000000001
#define inf 1<<29
using namespace std;
int n;
long long ans=1;
long long f[2][N/M]={0};
int keneng[N/M],kenengnum;
int sushu[N],sushunum;

void make_sushu(int limit) // 这里不是筛素数,而是把不是2,3的倍数的筛出来 
{                          // 如果筛素数,25不是素数,但是5的矩阵不能包含25 
    int i,j,k;
    int vis[N]={0};
    sushunum=0;
    for (i=1;i<=limit;i++)
        if (i%2!=0&&i%3!=0)
            sushu[++sushunum]=i;
}

bool shifou(int now)    // 预处理的判断,判断是否有两个1相连 
{                         // 即是否取连续的两个数
    int i,j,k;
    i=now&1; now>>=1;
    while (now!=0)
    {
        j=now&1;
        if (i==j&&i&&j)
            return false;
        i=j;
        now>>=1;
    }
    return true;
}

void chuli(int limit)  //预处理,把可以选的状态存起来,省时间 
{
    int i,j,k;
    int maxn=1;
    kenengnum=0;
    for (i=1;i<=limit;i++)
      maxn*=2;
    for (i=0;i<maxn;i++)
    {
        if (shifou(i))
        {
            keneng[kenengnum++]=i;
        }
    }
}

int pd_long(int x) // 由于矩阵每一行长度不同,所以预处理某些不能用 
{                  // 这个判断每一行长度 
    int i=1,zanshi=0;
    while (i<=x)
    {
        zanshi++;
        i*=3;
    }
    return zanshi;
}

long long make_juzhen(int now)
{
    int i,j,k,x=1,y,z;
    int limit,lnow,lnowbefore=inf;
    limit=pd_long(n/now);
    chuli(limit);
    f[0][0]=1;
    for (i=1;x*now<=n;i++,x*=2)
    {
        memset(f[i%2],0,sizeof(f[i%2]));
        lnow=pd_long(n/now/x);  // 当前行的长度 
        for (j=0;j<kenengnum;j++)
        {
            if (keneng[j]>>lnow>0) //如果有选超过当前行长度的,不选 
            {
                f[i%2][j]=0;
                continue;
            }
            for (k=0;k<kenengnum;k++) // 和上一行比较,是否有相连的1选了 
            {
                if (keneng[k]>>lnowbefore>0) // 上一行状态不能超过上一行长度 
                    continue;
                if (keneng[j]&keneng[k])
                    continue;
                f[i%2][j]+=f[(i+1)%2][k];
                f[i%2][j]%=yu;
            }
        }
        lnowbefore=lnow;  // 上一行长度 
    }
    return (f[(i+1)%2][0]+f[(i+1)%2][1])%yu; //最后一行一定只有一个,答案只有这两种情况加起来,最后一个选或不选 
}

int main()
{
    int i,j,k,x,y,z;
    cin>>n;
    make_sushu(n);
    for (i=1;i<=sushunum;i++)
    {
           ans*=make_juzhen(sushu[i]);
           ans%=yu;
    }
    cout<<ans<<endl;
}
原文地址:https://www.cnblogs.com/handsomeJian/p/3655608.html