bzoj 2734: [HNOI2012]集合选数 状压DP

2734: [HNOI2012]集合选数

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 560  Solved: 321
[Submit][Status]

Description

《集合论与图论》这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x 在该子集中,则 2x 和 3x 不能在该子集中。同学们不喜欢这种具有枚举性 质的题目,于是把它变成了以下问题:对于任意一个正整数 n≤100000,如何求出{1, 2,..., n} 的满足上述约束条件的子集的个数(只需输出对 1,000,000,001 取模的结果),现在这个问题就 交给你了。 
 

Input

 只有一行,其中有一个正整数 n,30%的数据满足 n≤20。 
 

Output


 仅包含一个正整数,表示{1, 2,..., n}有多少个满足上述约束条件 的子集。 
 

Sample Input


4

Sample Output

8

【样例解释】
 
有8 个集合满足要求,分别是空集,{1},{1,4},{2},{2,3},{3},{3,4},{4}。
 
  神奇的排列组合问题,其中分成多个独立子问题,分别转化为矩阵,最有用乘法原理合并的思想可以用在很多题里面。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100010
#define MOD 1000000001
typedef long long qword;
int gcd(int x,int y)
{
        return (x%y==0)?y:gcd(y,x%y);
}
int pow(int x,int y)
{
        int ret=1;
        while (y)
        {
                if (y&1)ret*=x;
                x*=x;
                y>>=1;
        }
        return ret;
}
qword pow_mod(qword x,int y)
{
        qword ret=1;
        while(y)
        {
                if (y&1)ret=ret*x%MOD;
                x=x*x%MOD;
                y>>=1;
        }
        return ret;
}
int dp[30][1<<12];
int ff[MAXN];
int main()
{
        //freopen("input.txt","r",stdin);
        int n,x,y;
        scanf("%d",&n);
        int i,j,k,ii;
        qword ans=1;
        memset(ff,-1,sizeof(ff));
        for (i=0;i<12;i++)
        {
                if ((1<<i)<MAXN)
                        ff[(1<<i)]=i;
        }
        for (i=0;i<MAXN;i++)
                if (ff[i]==-1)ff[i]=ff[i-1];
        for (ii=1;ii<=n;ii++)
        {
                if (ii%2==0 || ii%3==0)continue;
                int l,r,mid;
                l=0,r=12;
                while (l+1<r)
                {
                        mid=(l+r)>>1;
                        if ((qword)ii*pow(3,mid)<=n)
                                l=mid;
                        else
                                r=mid;
                }
                memset(dp,0,sizeof(dp));
                dp[0][0]=1;
                x=ii;
                for (i=1;ii*(1<<i>>1)<=n;i++)//log(n)
                {
                        for (j=0;j<(1<<r);j++)//2^(log3(n))
                        {
                                if (!dp[i-1][j])continue;
                                for (k=0;k<(1<<r);k++)
                                {
                                        if (j&k || (k&(k<<1)))continue;
                                        if ((qword)x*pow(3,ff[k])>n)break;
                                        dp[i][k]=(dp[i][k]+dp[i-1][j])%MOD;
                                }
                        }
                        x*=2;
                }
                qword res=0;
                for (j=0;j<(1<<r);j++)
                {
                        res=(res+dp[i-1][j])%MOD;
                }
                ans=ans*res%MOD;
        }
        printf("%lld\n",ans);
}

 

 
 

转载于:https://www.cnblogs.com/mhy12345/p/4109898.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值