• 【题解】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
    ", ans);
        return 0;
    }
  • 相关阅读:
    搞定 Linux Shell 文本处理工具,看完这篇还不够~
    ARM 版的Clang的使用
    GDB入门学习之gef插件的使用
    mac使用apktool
    python实现md5
    fridahookjava
    js hook array对象的push方法
    app逆向java转python代码
    python合并两个有序数组
    MySQL update 语句加锁分析
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/8969685.html
Copyright © 2020-2023  润新知