【多校训练】hdu 6125 Free from square 状压dp+分组背包

Problem Description
There is a set including all positive integers that are not more then  n . HazelFan wants to choose some integers from this set, satisfying: 1. The number of integers chosen is at least  1  and at most  k . 2. The product of integers chosen is 'free from square', which means it is divisible by no square number other than 1. Now please tell him how many ways are there to choose integers, module 10^9+7.
 

Input
The first line contains a positive integer  T(1T5) , denoting the number of test cases.
For each test case:
A single line contains two positive integers  n,k(1n,k500) .
 

Output
For each test case:
A single line contains a nonnegative integer, denoting the answer.
 

Sample Input
  
  
2 4 2 6 4
 

Sample Output
  
  
6 19

题意:

从不大于nn的所有正整数中选出至少11个且至多kk个使得乘积不包含平方因子,对109+7109+7取模。1≤n,k≤5001n,k500


思路:

把n个数分成若干组,互斥的放在同一组。

一开始把所有含平方因子的数去除掉,剩下的进行分组。

<sqrt(500)的八个素因子,编成八组,分别为包含2,3,5,7,11,13,17,19素因子的数。 注意包含的数不能重复。
>sqrt(500)的素因子,每一组为包含该素因子的数。
1这个数为一组.

因为其他组可能包含2,3,5,7,11,13,17,19;也就是这八个素因子在其他组也可能出现。

为了判断用状态压缩处理。

dp[k][s]表示选k个,前8个素因子选择状态为s时候的方法数。

dp[k][s] += dp[k-1][s-s1]; (s&s1==s1)

//
//  main.cpp
//  1006
//
//  Created by zc on 2017/8/29.
//  Copyright © 2017年 zc. All rights reserved.
//

#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long 
using namespace std;
const ll MOD=1e9+7;
const int N=505;
bool prime[N],v[N];
vector<pair<int,int> >r[N];
int cnt=0,p[N];
ll d[N][1<<8];
void init()
{
    for(int i=2;i<=N;i++)
    {
        if(prime[i]==0)
        {
            for(int j=i;j<=N;j+=i)  prime[j]=1;
            p[++cnt]=i;
        }
    }
    memset(v,0,sizeof(v));
    for(int i=2;i<=N;i++)
    {
        if(v[i])    continue;
        for(int j=1;j<=8;j++)
        {
            if(i%(p[j]*p[j])==0)
            {
                v[i]=1;
                break;
            }
        }
    }
    for(int i=0;i<=cnt;i++)   r[i].clear();
    r[0].push_back(make_pair(1,0));
    for(int i=cnt;i>0;i--)
    {
        for(int j=p[i];j<=N;j+=p[i])
        {
            if(v[j]) continue;
            int s=0;
            for(int k=0;k<8;k++)
            {
                if(j%p[k+1]==0)   s|=(1<<k);
            }
            r[i].push_back(make_pair(j,s));
            v[j]=1;
        }
    }
}

ll solve(int n,int m)
{
    memset(d,0,sizeof(d));
    d[0][0]=1;
    int ed=1<<8;
    for(int i=0;i<=cnt&&p[i]<=n;i++)
        for(int j=m;j>0;j--)
            for(int k=0;k<r[i].size();k++)
            {
                if(r[i][k].first>n)   continue;
                for(int s=0;s<ed;s++)
                {
                    if((s&r[i][k].second)==r[i][k].second)
                    {
                        d[j][s]=(d[j][s]+d[j-1][s-r[i][k].second])%MOD;
                    }
                }
            }
    ll ans=0;
    for(int i=1;i<=m;i++)
        for(int j=0;j<ed;j++)
            ans=(ans+d[i][j])%MOD;
    return ans;
}

int main(int argc, const char * argv[]) {
    int T,n,k;
    init();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        printf("%lld\n",solve(n,k));
    }
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值