【状压DP】【cofun1623】寿司晚宴

来源:互联网 发布:网络文明志愿者注册 编辑:程序博客网 时间:2024/04/29 12:14

【cofun1623】寿司晚宴

这里写图片描述

Sample Input
输入样例1
3 10000
输入样例2
4 10000
输入样例3
100 100000000
Sample Output
样例输出1
9
样例输出2
21
样例输出3
3107203

Hint
这里写图片描述


  • 分析:
    题意:把一个数的集合分为两个集合,记为G,M,
    显然,G中数的质因子与M中数的质因子应互不相同,因此选了一个数等同于选择了它的质因子。
    虽然2<=n<=500,数据比较大,但是经过计算,我们会发现每个数最多只能有一个>=n 的质因子,其余质因子都< n ;而< n 的质数只有8个,加上对题目理解,可以考虑状压DP。

    1. 把每个数中<500 的质因子压进二进制数中,并把它>=500 的质因子记录下来。
    2. 按照最大质因子的大小给寿司排序,方便DP。
    3. 状压DP:
      令f[i][j]表示集合G中质因子状态为i,集合M中质因子状态为j的方案数;dp[0][i][j]表示新出现的大质数归入集合G,原先G状态为i,M状态为j的方案数;dp[1][i][j]表示新出现的大质数归入集合M,原先G状态为i,M状态为j的方案数;
      当枚举到新的大质数时,分情况,即不归入(不DP),归入G和归入M,可平行状压DP;
      当含该新大质数的状态枚举完后,应将dp[0][i][j]和dp[1][i][j]归并为f[i][j],注意归并时因dp[0][i][j]和dp[1][i][j]都包含了不归入新的大质数的状态,应再减去一次,因类似背包,直接-f[i][j]即可。
      注意:运用背包思想降了一维是表示当前枚举到第几个大质数,所以dp转移时要倒序。
      具体转移方程在代码中注解。【因为比较多判断条件,会影响美观XDDD

【做这题的时候看了dalao们的题解,感觉不是很能理解,还是自己打舒服~


  • 代码:
#include <bits/stdc++.h> using namespace std; const int su[8] = {2, 3, 5, 7, 11, 13, 17, 19}; struct info{    int da, su; }e[505]; long long  n, m, p, i, j, k, ans, dp[2][1 << 8][1 << 8], f[1 << 8][1 << 8];  inline int read() {    long long x = 0, w = 1;    char ch = 0;    while(ch < '0' || ch > '9')    {        if (ch == '-')            w = -1;        ch = getchar();     }    while(ch >= '0' && ch <= '9')          x = x * 10 + ch - '0', ch = getchar();    return x * w;  } //读入优化 inline void write(long long x) {    if (x < 0)        putchar('-'), x = -x;    if (x > 9)        write(x / 10);    putchar(x % 10 + '0'); }//输出优化 bool cmp(info a, info b) {    return a.da < b.da || (a.da == b.da && a.su < b.su); } int main() {    n = read(), p = read();    //读入     for(i = 2; i <= n; i ++)    {        m ++;        for(j = 0, k = i; j < 8; j ++)        if (k % su[j] == 0)        {            for(; k % su[j] == 0; k /= su[j]);            e[m].su |= (1 << j);         }                  e[m].da = k;     }    sort(e + 1, e + m + 1, cmp);    //把美味度的质因子压进二进制并按大质数排序1.&2.     f[0][0] = 1;    for(i = 1; i <= m; i ++)    {        if (e[i].da == 1 || e[i].da != e[i - 1].da)        {            memcpy(dp[0], f, sizeof(f));            memcpy(dp[1], f, sizeof(f));         }        //新出现的大质数不归入集合G与M         for(j = (1 << 8) - 1; j >= 0; j --)            for(k = (1 << 8) - 1; k >= 0; k --)            {                if (! (e[i].su & k))                    dp[0][j | e[i].su][k] = (dp[0][j | e[i].su][k] + dp[0][j][k]) % p;                //新出现的大质数归入集合G                 if (! (e[i].su & j))                    dp[1][j][k | e[i].su] = (dp[1][j][k | e[i].su] + dp[1][j][k]) % p;                //新出现的大质数归入集合M              }        //新出现的大质数归入集合G或M,同时将小质数的状态进行状压DP             if (e[i].da == 1 || e[i].da != e[i + 1].da)            for(j = 0; j < 1 << 8; j ++)                for(k = 0; k < 1 << 8; k ++)                    f[j][k] = ((dp[0][j][k] - f[j][k]) % p + dp[1][j][k] + p) % p;        //将两种平行的情况归并      }    //DP3.     for(j = 0; j < 1 << 8; j ++)        for(k = 0; k < 1 << 8; k ++)        if (! (j & k))            ans = (ans + f[j][k]) % p;    //统计答案     write(ans);    //输出     return 0; }

下午好咩咩咩鹿啦啦~


已经晚上19:23了,上来抒发一下内心的咩咩皮(MMP),我在下一篇【队伍统计】中写跟同学讲了蛮久的寿司晚宴,然后,刚刚才帮他把这题啪对。。很多小细节都会导致致命的错误,他居然在转移方程里多了个+号,看了贼久ODX所以做题时一定要仔细啊!!!!!

原创粉丝点击