P2150 [NOI2015] 寿司晚宴

传送门

做完了卡牌,便寻思着来加强一下这种套路

结果关键处(根号分治)一样之外,其他做法是完全不同的......


思路

对于 n 比较小的情况,我们可以考虑进行状压DP

一个集合 S 存着一些质数,表示选择的数中的质因子含有这些数

我们可以设计一个状态 dp[S1][S2],表示甲选择的数的质因子为 S1,乙选择的数的质因子为 S2,且 S1S2=

我们考虑每个数进行转移,设 i 分解质因子的集合为 k,那么就有转移:

dp[i][S1|k][S2]+=dp[i1][S1][S2], kS2=

dp[i][S1][S2|k]+=dp[i1][S1][S2], kS1=

n 比较大时,我们就无法全部进行状压来DP

但我们可以观察到,每个数最多只会含有一个大于 n 的质因子

我们先将序列按照最大质因子进行排序,对最大质因子相同的数一起处理

我们令 r1[S1][S2] 表示甲选择这种最大质因子的方案数,r2[S1][S2] 表示乙选择这种最大质因子的方案数

我们开始先将 dp 复制到 r1r2,然后对 r1r2 进行转移:

r1[S1|k][S2]+=r1[S1][S2]

r2[S1][S2|k]+=r2[S1][S2]

然后我们统计到 dp 的答案就为:

dp[S1][S2]=r1[S1][S2]+r2[S1][S2]dp[S1][S2]

这里之所以要减去一个 dp[S1][S2],是因为 r1r2 都包含了不选这种最大质因数的方案,重复了

最后将所有状态统计答案即可


代码

#include<iostream> #include<fstream> #include<algorithm> #include<cmath> #include<cstdlib> #include<cstring> #include<queue> #include<map> #include<set> #include<bitset> #define LL long long inline LL reads() { LL sign = 1, re = 0; char c = getchar(); while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();} while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();} return sign * re; } struct Node { int num, Mx, st; }a[505]; inline bool cmp(Node a, Node b){return a.Mx < b.Mx;} LL n, mod, sq; inline void add(LL &a, LL b) {a + b >= mod ? a += b - mod : a += b;} inline LL radd(LL a, LL b) {return a + b >= mod ? a + b - mod : a + b;} inline LL sub(LL a, LL b) {return a - b < 0 ? a - b + mod : a - b;} int prime[505], pcnt, scnt; std::bitset<505> vis; inline void Init() { for(int i = 2; i <= n; i++) { if(!vis[i]) { prime[++pcnt] = i; if(i <= sq) scnt++; } for(int j = 1; j <= pcnt && i * prime[j] <= n; j++) { int k = i *prime[j]; vis[k] = true; if(!(i % prime[j])) break; } } for(int i = 2; i <= n; i++) { int rp = i; for(int j = 1; j <= scnt; j++) if(!(rp % prime[j])) { a[i].st |= 1 << (j - 1); while(!(rp % prime[j])) rp /= prime[j]; } if(rp ^ 1) a[i].Mx = rp; } } LL dp[520][520], r1[520][520], r2[520][520], ans; signed main() { #ifndef ONLINE_JUDGE freopen("test.in", "r", stdin); freopen("test.out", "w", stdout); #endif n = reads(), mod = reads(); sq = sqrt(n); Init(); for(int i = 2; i <= n; i++) a[i].num = i; std::sort(a + 2, a + 1 + n, cmp); dp[0][0] = 1; for(int i = 2; i <= n; i++) { if(!a[i].Mx || a[i].Mx != a[i - 1].Mx) memcpy(r1, dp, sizeof(dp)), memcpy(r2, dp, sizeof(dp)); for(int j = (1 << scnt) - 1; j >= 0; j--) for(int k = (1 << scnt) - 1; k >= 0; k--) { if(j & k) continue; if(!(a[i].st & k)) add(r1[j | a[i].st][k], r1[j][k]); if(!(a[i].st & j)) add(r2[j][k | a[i].st], r2[j][k]); } if(!a[i].Mx || a[i].Mx != a[i + 1].Mx) { for(int j = 0; j < (1 << scnt); j++) for(int k = 0; k < (1 << scnt); k++) { if(j & k) continue; dp[j][k] = sub(radd(r1[j][k], r2[j][k]), dp[j][k]); } } } for(int i = 0; i < (1 << scnt); i++) for(int j = 0; j < (1 << scnt); j++) if(!(i & j)) add(ans, dp[i][j]); printf("%lld", ans); return 0; }

__EOF__

本文作者zuytong
本文链接https://www.cnblogs.com/zuytong/p/16251022.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zuytong  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示