hdu 6125 (状压dp+分组背包)

这道题思维很奇特。。。

题目大意:从给出的n个数中选出至少一个,至多k个,保证这些数的乘积没有一个因数

是完全平方数。也就是说没有一个质因数出现超过1次。

n和k小于等于500。那么因为根号500以内的质因数只有8个,很容易想到状压,

把每个数的质因数按照是否出现压成一个二进制数,如果出现多次,这个数就不可用了

之后枚举每个数,枚举每个状态,可以从当前状态向下转移当且仅当当前状态和枚举的

数字没有重复的质因数,最后求出状态的和。

高高兴兴的写了

Wrong Answer

好了,什么都没看到,很显然当同时出现23和46时这样根号500以上的质因数和他的

倍数时,这么枚举是会多情况的,但是又不能压缩所有的质因数,怎么办?

这是就可以分组背包了,把前8个质因数分出来计算,其余的按每个质因数和他的倍数

压在一起,计算时先用与后面质因数无关的数(也就是至于前八个质因数有关的

,在第一个背包里),他们可以压在一起计算,不用每个数都自己一个包(因为要

保证包内部数字不能同时出现)。

这样无需判断23和46这种问题,因为他们在同一个包里,不能互相取。

之后按上面的方法枚举就好了,注意空间开不下,第一位要用滚动数组。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define mode 1000000007
using namespace std;
typedef long long ll;
int n,k;
int pri[105];
bool used[505];
int unc[505];
int cnt = 0;
ll f[2][(1<<8)+1][505];
int bag[105][505];
int cct[105];
int sit[505];
void gett()
{
    for(int i = 2;i <= 500;i++)
    {
        if(!used[i])pri[cnt++] = i;
        for(int j = 0;j < cnt&&i*pri[j] <= 500;j++)
        {
            used[i*pri[j]] = 1;
            if(!(i%pri[j]))break;
        }
    }
}
void gba()
{
    bag[1][++cct[1]] = 1;
    for(int i = 2;i <= 500;i++)
    {
        bool flag = 0;
        for(int j = 8;j < cnt&&i >= pri[j];j++)
        {
            if(!(i%pri[j]))
            {
                flag = 1;
                bag[j-6][++cct[j-6]] = i;
                break;
            }
        }
        if(!flag)
        {
            bag[1][++cct[1]] = i;
        }
    }
}
void gej()
{
    sit[1] = 0;
    for(int i = 2;i <= 500;i++)
    {
        int ty = i;
        for(int j = 0;j < 8;j++)
        {
            int ww = 0;
            while(ty%pri[j] == 0)
            {
                ww++;
                ty /= pri[j];
            }
            if(ww >= 2)
            {
                unc[i] = 1;
                break;
            }else if(ww == 1)
            {
                sit[i] |= (1<<j);
            }
        }
    }
}
int main()
{
    int T;
    scanf("%d", &T);
    gett();
    gba();
    gej();
    while(T--)
    {
        scanf("%d%d",&n, &k);
        memset(f,0,sizeof(f));
        f[0][0][0] = 1;
        int now = 1,past = 0;
        for(int i = 1;i <= cct[1];i++)
        {
            memset(f[now],0,sizeof(f[now]));
            for(int j = 0;j <= (1<<8)-1;j++)
            {
                for(int t = 0;t <= k;t++)
                {
                    f[now][j][t] = f[past][j][t];
                }
            }
            for(int j = 0;j <= (1<<8)-1;j++)
            {
                for(int t = 0;t < k;t++)
                {
                    if(!(sit[bag[1][i]]&j)&&!unc[bag[1][i]]&&bag[1][i]<=n)
                    {
                        f[now][j|sit[bag[1][i]]][t+1] += f[past][j][t];
                        f[now][j|sit[bag[1][i]]][t+1] %= mode;
                    }

                }
            }
            swap(now,past);
        }
        for(int i = 2;i < cnt-6;i++)
        {
            memset(f[now],0,sizeof(f[now]));
            for(int j = 0;j <= (1<<8)-1;j++)
            {
                for(int t = 0;t <= k;t++)
                {
                    f[now][j][t] = f[past][j][t];
                }
            }
            for(int u = 1;u <= cct[i];u++)
            {
                for(int j = 0;j <= (1<<8)-1;j++)
                {
                    for(int t = 0;t < k;t++)
                    {
                        if(!(sit[bag[i][u]]&j)&&!unc[bag[i][u]]&&bag[i][u]<=n)
                        {
                            f[now][j|sit[bag[i][u]]][t+1] += f[past][j][t];
                            f[now][j|sit[bag[i][u]]][t+1] %= mode;
                        }
                    }
                }
            }
            swap(now,past);
        }
        ll ans = 0;
        for(int i = 0;i <= (1<<8)-1;i++)
        {
            for(int j = 1;j <= k;j++)
            {
                ans += f[past][i][j];
                ans %= mode;
            }
        }
        printf("%I64d\n",ans%mode);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zzk_233/article/details/81778141