题解NOI2015寿司晚宴

Posted Twilight_Sx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解NOI2015寿司晚宴相关的知识,希望对你有一定的参考价值。

  想好久啊+不敢写啊……但果然人还是应当勇敢自信,只有坚定地去尝试,才会知道最后的结果。1A真的太开心啦,不过好像我的做法还是比较复杂的样子……理解起来应该算是比较容易好懂的类型,大家可以参考一下思路~

  首先我们先考虑一下简单的30分算法:30以内的质数只有十个左右,可以利用状压表示出两个人所选择的集合,再通过寿司转移即可。之后的大数据呢?我们发现不能这样做是因为之后的质数越来越多,状压的空间就开不下了。

  这时要注意到一个性质:对于1~n内的每一个数而言,都可以分解成若干个<sqrt(n)的质数之积 || 在此基础之上再乘上一个 > sqrt(n)的质数。这说明什么?对于所有的>sqrt(n)的质数而言,我们选择一个寿司只可能选择其中的一个——换句话说,就是不同的大质数之间的决策是相互独立的。

  于是就有了如下算法:既然不同的大质数之间不会互相影响,我们就一个一个大质数来统计,之后再累加到一起即可。于是我们增加一维的状态,单独表示这一个大质数。0表示两个集合中均不含有这个大质数因子,1表示第一个人所选择的集合中含这个因子,2表示第二个人选择的集合中含有这个因子。不同的因子之间的转移将所有1&2的状态都加入0并清空1&2即可(对于新的质数来说,之前没有作出过相应的决策,所以是不含有该因子的)。

  网上代码很短,然而我莫名长……

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000
#define maxt 260
#define int long long
int n, mod, S[maxn], CNST = (1 << 8) - 1;
int cnt, dp[3][maxt][maxt], num[maxn], mark[maxn];
int tot, P[maxn], cnp = 8, ans;
int pri[maxn] = {0, 2, 3, 5, 7, 11, 13, 17, 19};
map <int, int> Map;

int read()
{
    int x = 0;
    char c;
    c = getchar();
    while(c < 0 || c > 9) c = getchar();
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar();
    return x;
}

int up(int &x, int y)
{
    x += y;
    if(x >= mod) x -= mod;
}

void work(int i)
{
    for(int x = CNST; ~x; x --)
        for(int y = CNST; ~y; y --)
        {
            if(x & y) continue;
            int a = dp[1][x][y], b = dp[2][x][y];
            up(dp[1][x | num[i]][y], dp[0][x][y]); 
            up(dp[2][x][y | num[i]], dp[0][x][y]);
            
            up(dp[1][x | num[i]][y], a);
            up(dp[2][x][y | num[i]], b);
        }
}

void DP(int k)
{
    for(int x = 0; x <= CNST; x ++)
        for(int y = 0; y <= CNST; y ++)
            if(x & y) continue;
            else 
            {
                up(dp[0][x][y], dp[1][x][y]);
                up(dp[0][x][y], dp[2][x][y]);
                dp[1][x][y] = dp[2][x][y] = 0;
            } 
    if(k) for(int i = k; i <= n + 1; i += k) work(i);
    else 
    {
        for(int i = 1; i <= cnt; i ++) 
            for(int x = CNST; ~x; x --)
                for(int y = CNST; ~y; y --)
                {
                    if(x & y) continue;
                    int k = S[i];
                    int a = dp[0][x][y];
                    up(dp[0][x | num[k]][y], a); 
                    up(dp[0][x][y | num[k]], a);
                }
    }
}

signed main()
{
    n = read() - 1, mod = read();
    dp[0][0][0] = 1;
    for(int i = 2; i <= n + 1; i ++)
    {
        int k = i;
        for(int j = 1; j <= cnp; j ++)
        {
            if(!(k % pri[j])) num[i] |= (1 << (j - 1));
            while(!(k % pri[j])) k /= pri[j];
        }
        if(k != 1 && k != 0) 
        {
            mark[i - 1] = k;
            if(!Map[k]) Map[k] = 1, P[++ tot] = k;
        }
        else S[++ cnt] = i;
    } 
    for(int i = 0; i <= tot; i ++) 
        DP(P[i]);
    for(int i = 0; i <= CNST; i ++)
        for(int j = 0; j <= CNST; j ++)
            if(i & j) continue;
            else
            {
                up(ans, dp[0][i][j]);
                up(ans, dp[1][i][j]);
                up(ans, dp[2][i][j]);
            }
    printf("%lld\n", ans);
    return 0;
}

 

以上是关于题解NOI2015寿司晚宴的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj4197][Noi2015]寿司晚宴

状压DP --- [NOI2015]寿司晚宴

NOI2015寿司晚宴

[NOI2015]寿司晚宴

[UOJ#129][BZOJ4197][Noi2015]寿司晚宴

[BZOJ]4197: [Noi2015]寿司晚宴