HDU 6125 - Free from square | 2017 Multi-University Training Contest 7

本文提供HDU6125-Freefromsquare题目的详细解答过程,利用分组、状压和动态规划等算法解决不超过N的数字中选K个,其乘积不是平方数的倍数的问题。分析了小于根号N的质因子及其对解的影响。

思路来自这里 - -

 

/*
HDU 6125 - Free from square [ 分组,状压,DP ]  |  2017 Multi-University Training Contest 7
题意:
	不超过N的数字中选K个,其乘积不是平方数的倍数
	限制 N,K <= 500
分析:
	小于根号N的质因子至多只有8个,而大于根号N的质因子任意两个乘积大于N
	所以N以内的完全平方数只有两种 
		1. 没有大于根号N的质因子
		2. 有且只有1个大于根号N的质因子
	对于小于根号N的质因子,可以直接按集合状压DP(自身为1组)
	对于大于根号N的质因子,可以将包含该质因子的 非平方数的倍数的数 都归为1组,然后分组DP
	每个数字的状态为其所包含的小于根号N的质因子的集合
	具体DP递推式 为 
		if (a&b == 0) dp[k][a|b] = (dp[k][a|b] + dp[k-1][b]) % MOD;
	其中 a, b为小于8的质因子的集合
*/
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9+7;
const int N = 505;
int p[8] = {2, 3, 5, 7, 11, 13, 17, 19};
int n, t, k;
int dp[N][1<<10];
int st[N], belong[N];
vector<int> v[N];
int solve()
{
    for (int i = 1; i <= n; i++)
    {
        belong[i] = i;
        for (int j = 0; j < 8; j++)
            if (i % p[j] == 0)
            {
                if (i% (p[j]*p[j]) == 0)
                {
                    st[i] = -1; break;
                }
                st[i] |= 1<<j;
                belong[i] /= p[j];
            }
        if (st[i] == -1) continue;
        if (belong[i] == 1) v[i].push_back(i);
        else v[belong[i]].push_back(i);
    }
    dp[0][0] = 1;
    for (int i = 1; i <= n; i++)
    {
        if (st[i] == -1 || v[i].size() == 0) continue;
        for (int l = k; l >= 1; l--)
            for (int j = 0; j < (1<<8); j++)
                for (auto & x : v[i])
                {
                    int p = st[x];
                    if (!(p&j)) dp[l][p|j] = (dp[l][p|j] + dp[l-1][j]) % MOD;
                }
    }
    int ans = 0;
    for (int i = 1; i <= k; i++)
        for (int j = 0; j < (1<<8); j++)
            ans = (ans + dp[i][j]) % MOD;
    return ans;
}
int main()
{
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d%d", &n, &k);
        memset(dp, 0, sizeof(dp));
        memset(st, 0, sizeof(st));
        for (int i = 1; i <= n; i++) v[i].clear();
        printf("%d\n", solve());
    }
}

  

转载于:https://www.cnblogs.com/nicetomeetu/p/7376091.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值