洛谷 P2150 [NOI2015]寿司晚宴 dp

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/82503498

题目描述
为了庆祝NOI的成功开幕,主办方为大家准备了一场寿司晚宴。小G和小W作为参加NOI的选手,也被邀请参加了寿司晚宴。
在晚宴上,主办方为大家提供了n−1种不同的寿司,编号1,2,3,⋯,n-1,其中第种寿司的美味度为i+1(即寿司的美味度为从2到n)。
现在小G和小W希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小G品尝的寿司种类中存在一种美味度为x的寿司,小W品尝的寿司中存在一种美味度为y的寿司,而x与y不互质。
现在小G和小W希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数p取模)。注意一个人可以不吃任何寿司。

输入输出格式

输入格式:
从文件dinner.in中读入数据。

输入文件的第1行包含2个正整数n,p中间用单个空格隔开,表示共有n种寿司,最终和谐的方案数要对p取模。

输出格式:
输出到文件dinner.out中。

输出一行包含1个整数,表示所求的方案模p的结果。

输入输出样例

输入样例#1:
3 10000
输出样例#1:
9
输入样例#2:
4 10000
输出样例#2:
21
输入样例#3:
100 100000000
输出样例#3:
3107203

说明
n <= 500   p <= 10 9

分析:
一种合法的选法,那么他们的积互质,也就是他们的质因数分解没有交集。
但是 500 个里的质数比较多,无法直接状压。我们知道,一个数 x 大于 x 的质因数最多只有一个,而 500 内的小于 n 质因数只有8个,这样就可以状压了。
我们记录一下一个数大于 n 的质因数具体是哪个,然后依据这个排序。对于大质数相同的,只能给其中一个人选,设两个数组 f 1 f 2 表示第一个人和第二个人选的状态,然后合并就可以,但是同时不选这个状态算重,要减掉这个状态的初始值。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long

const int maxn=507;
const int prime[8]={2,3,5,7,11,13,17,19};

using namespace std;

int n;
LL p;
LL f[maxn][maxn],f1[maxn][maxn],f2[maxn][maxn];

struct rec{
    int s;
    int x;
}a[maxn];

bool cmp(rec a,rec b)
{
    return a.x<b.x;
}

int main()
{
    scanf("%d%lld",&n,&p);
    for (int i=2;i<=n;i++)
    {
        int x=i;
        for (int j=0;j<8;j++)
        {
            if (x%prime[j]==0)
            {
                a[i].s|=1<<j;
                while (x%prime[j]==0) x/=prime[j];  
            }
        }
        a[i].x=x;
    }
    sort(a+2,a+n+1,cmp);
    int s=1<<8;
    f[0][0]=1;
    int g=0;
    for (int i=2;i<=n;i++)
    {
        if ((a[i].x==1) || (a[i].x!=a[i-1].x))
        {
            for (int j=0;j<s;j++)
            {
                for (int k=0;k<s;k++)
                {
                    f1[j][k]=f[j][k];
                    f2[j][k]=f[j][k];
                }
            }
        }       
        for (int j=s;j>=0;j--)
        {
            for (int k=s;k>=0;k--)
            {
                if ((k&a[i].s)==0) f1[j|a[i].s][k]=(f1[j|a[i].s][k]+f1[j][k])%p;
                if ((j&a[i].s)==0) f2[j][k|a[i].s]=(f2[j][k|a[i].s]+f2[j][k])%p;
            }
        }
        if ((a[i+1].x==1) || (a[i].x!=a[i+1].x))
        {
            for (int j=0;j<s;j++)
            {
                for (int k=0;k<s;k++)
                {
                    f[j][k]=(f1[j][k]+f2[j][k]+p-f[j][k])%p;
                }
            }
        }
    }   
    LL ans=0;
    for (int i=0;i<s;i++)
    {
        for (int j=0;j<s;j++)
        {
            if ((i&j)==0) ans=(ans+f[i][j])%p;
        }
    }
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/82503498