Description

为了庆祝 NOI 的成功开幕, 主办方为大家准备了一场寿司晚宴. 小 G 和小 W 作为参加 NOI 的选手, 也被邀请参加了寿司晚宴.

在晚宴上, 主办方为大家提供了 \(n - 1\) 种不同的寿司, 编号 \(1, 2, 3, \ldots, n-1\), 其中第 \(i\) 种寿司的美味度为 \(i+1\) (即寿司的美味度为从 \(2\)\(n\)) .

现在小 G 和小 W 希望每人选一些寿司种类来品尝, 他们规定一种品尝方案为不和谐的当且仅当: 小 G 品尝的寿司种类中存在一种美味度为 \(x\) 的寿司, 小 W 品尝的寿司中存在一种 美味度为 \(y\) 的寿司, 而 \(x\)\(y\) 不互质.

现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案 (对给定的正整数 \(p\) 取模). 注意一个人可以不吃任何寿司.

Input

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

Output

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

Sample Input

3 10000

Sample Output

9

Data Range

对于所有的数据, 满足:\(2 \leq n \leq 500, 0 \lt p \leq 10^9\)

Explanation

这道题首先想一下发现可以直接搞出状压 DP 因为本来 \((1, n)\) 以内就没有多少个质数对吧......

但是这样想虽然接近正解但是还是错的.

为什么呢? 因为这样存在状压里面还是会强行 MLE, 因为状态实在太多了.

但是我们发现太大的质数本来就没有多少, 而且可以强行被拆出来单独考虑, 所以我们就不把大质数放在状态里面了, 单独再开一个维度保存.

按照最大的不可以被分解的质因子排一下序, 然后得出来的都是按照最糟糕的那个因子考虑 的元素.

由此, 我们就可以考虑这个大质数到底应该分给谁了.

很显然这个状态压缩可以给满分~

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>

#define rep(_var,_begin,_end) for(int _var=_begin;_var<=_end;_var++)
#define per(_var,_begin,_end) for(int _var=_begin;_var>=_end;_var--)
using namespace std;
typedef long long lli;
const int maxn = 510, maxm = 1<<8;

struct srtr {
    lli a; // State / binary representation of small primes
    lli p; // Largest (remaining) prime factor
}; bool operator < (srtr a, srtr b) {
    return a.p < b.p;
}

int primes[8] = { 2, 3, 5, 7, 11, 13, 17, 19 };
srtr factor(int x)
{
    int a = 0, x_ = x;
    for (int i = 0; i < 8; i++) {
        int v = primes[i];
        while (x % v == 0) {
            x /= v;
            a |= (1 << i);
        }
    }
    srtr ret; ret.a = a, ret.p = x;
    return ret;
}

int n;
lli modr;
srtr arr[maxn];

lli dp[maxn][maxn][2],
    f[maxn][maxn]; // Practically a buffer

int main(int argc, char** argv)
{
    scanf("%d%lld", &n, &modr);
    // Pre-processing factors of all numbers
    for (int i = 2; i <= n; i++)
        arr[i] = factor(i);
    sort(arr + 2, arr + n + 1);
    // Dynamic programming.
    f[0][0] = 1;
    for (int i = 2; i <= n; i++) {
        // Assigning datum
        if (i == 2 || arr[i].p == 1 || arr[i].p != arr[i-1].p)
            per(j, maxm, 0) per(k, maxm, 0)
                dp[j][k][0] = dp[j][k][1] = f[j][k];
        // Modifying from previous stati
        per(j, maxm, 0) per(k, maxm, 0) {
            if ((arr[i].a & k) == 0) // Can assign to left
                dp[j | arr[i].a][k][0] = (dp[j | arr[i].a][k][0] + dp[j][k][0]) % modr;
            if ((arr[i].a & j) == 0) // Can assign to right
                dp[j][k | arr[i].a][1] = (dp[j][k | arr[i].a][1] + dp[j][k][1]) % modr;
        }
        // Copying to f[][] array
        if (i == n || arr[i].p == 1 || arr[i].p != arr[i+1].p)
            per(j, maxm, 0) per(k, maxm, 0)
                f[j][k] = (dp[j][k][0] + dp[j][k][1] - f[j][k] + modr) % modr;
    }
    // Flush DP results to buffer
    lli res = 0;
    rep(i, 0, maxm) rep(j, 0, maxm)
        if ((i & j) == 0)
            res = (res + f[i][j] + modr) % modr;
    // Output
    printf("%lld\n", res);
    return 0;
}