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;

}

 

你可能感兴趣的:(2012)