Free from square

HDU - 6125
这里写图片描述

从 1-n 选出不超过 k 个数,使得所有数乘积不能被任何平方数整除。
一开始算了一下500以内的质数,还是挺多的,但是注意到 500 中的素数只有8个,那么就可以做装压dp了,对于含有超过 500 的素数,进行因式分解,并且把拥有相同超过 500 的质数放在一起(肯定最多只有一个),然后dp的时候通过调换枚举顺序就可以保证同一组的肯定不会同时更新一个状态,即保证了同一组不会出现到一起。

#include <bits/stdc++.h>
#include <vector>
using namespace std;

const int maxn = 500;
const int mo = 1e9+7;
int p[maxn+10], f[maxn+10], ans, n, m, a[maxn+10][maxn+10], ff[maxn+1];
bool flag[501];
vector<int> G[maxn];
void getPrime() {
    memset(p, 0, sizeof(p));
    for (int i = 2; i <= maxn; i++) {
        if (!p[i]) p[++p[0]] = i;
        for (int j = 1; j <= p[0] && p[j] <= maxn/i; j++) {
            p[p[j]*i] = 1;
            if (i%p[j] == 0) break;
        }
    }
}

int get(int x) {
    if (!flag[x]) return -1;
    int t = 0, y = 1;
    for (int i = 1; i <= 8; i++) {
        if (x%p[i] == 0) t += y;
        y *= 2;
    }
    return t;
}

void init() {
    memset(flag, true, sizeof(flag));
    for (int i = 1; i <= 500; i++) G[i].clear();
    for (int i = 1; i <= p[0]; i++) {
        for (int j = 1; j*p[i]*p[i] <= maxn; j++) flag[j*p[i]*p[i]] = false;
    }
    for (int i = 1; i <= 500; i++) f[i] = get(i);
    for (int i = 9; i <= p[0]; i++) {
        for (int j = 1; j*p[i] <= maxn; j++) if (flag[j*p[i]]) {
            flag[j*p[i]] = false;
            G[i-8].push_back(p[i]*j);
        }
    }
}

int main() {
    getPrime();
    init();
    int T;
    for (int i = 0; i <= maxn; i++) ff[i] = f[i];
    scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &n, &m);
        memset(a, 0, sizeof(a));
        a[0][0] = 1;
        for (int k = 1; k <= n; k++) if (flag[k]) {
            int t = f[k];
            for (int i = 255; i >= 0; i--)
                for (int j = m-1; j >= 0; j--) if (a[i][j] && (t&i) == 0) {
                    a[t|i][j+1] += a[i][j];
                    a[t|i][j+1] %= mo;
                }
        }
        for (int k = 1; k <= p[0]-8; k++) {
            for (int i = 255; i >= 0; i--)
                for (int j = m-1; j >= 0; j--) if (a[i][j]) {
                    for (int x = 0; x < G[k].size(); x++) {
                        int t = f[G[k][x]];
                        if ((i&t) == 0 && G[k][x] <= n) {
                            a[i|t][j+1] += a[i][j];
                            a[i|t][j+1] %= mo;
                        }
                    }
                }
        }
        a[0][0] = 0;
        ans = 0;
        for (int i = 0; i <= 255; i++) 
            for (int j = 1; j <= m; j++) ans = (ans+a[i][j])%mo;
        printf("%lld\n", ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值