NOI2015 寿司晚宴 解题报告
在晚宴上,主办方为大家提供了
n−1 种不同的寿司,编号1,2,3,\ldots,n-1 ,其中第种寿司的美味度为i+1 。(即寿司的美味度为从2 到n )现在小 G 和小 W 希望每人选一些寿司种类来品尝,他们规定一种品尝方案为不和谐的当且仅当:小 G 品尝的寿司种类中存在一种美味度为
x 的寿司,小 W 品尝的寿司中存在一种美味度为y 的寿司,而x 与y 不互质。现在小 G 和小 W 希望统计一共有多少种和谐的品尝寿司的方案(对给定的正整数
p 取模)。注意一个人可以不吃任何寿司。
n \leq 300,p \leq 10^{10}
解题思路:
状压 DP。
对于 30 分的部分分,可以考虑设计一个暴力 DP。这里使用一个小 trick,我们只需要记录每个数的质因子即可,而在 30 以内的质数一共只有 10 个,故我们不妨设计
那么转移的时候我们就考虑
这个转移是比较简单的,时间复杂度
接下来考虑满分做法,我们发现瓶颈在于我们直接把所有质因子压进了状态,这样在
这就要用到本题启发的第二个 trick:对于具有特殊性的一些因子单独处理。具体来说,我们发现所有
所以对于
-
如果
p 相同,那么我们单独考虑把这个数分给s_1,s_2 的方案数,但是这个结果不能并入f 数组中,因为对于后面的kp 会造成影响。所以我们不妨新建两个数组g,h 来表示把该数分入s_1,s_2 的方案数。对于
g 来说(对于h 是类似的),我们考虑一共两种情况:- 在已经有
p 的基础上加入这个数,那么这部分的方案就是g_{i-1,s_1,s_2} - 在没有
p 的基础上加入这个数,这部分的方案就是f_{i,s_1,s_2}
个人认为这种思路是比题解里大部分的容斥更加自然的。
- 在已经有
- 如果
p 不同,我们直接继承上一段的状态,也就是f 正常更新,把g,h 赋值为f 即可。
这样的时间复杂度是很优秀的!达到了
代码:
const int N = 510;
const int px[8] = {2,3,5,7,11,13,17,19};
int f[2][(1<<8)][(1<<8)],g[2][(1<<8)][(1<<8)],w[2][(1<<8)][(1<<8)],mod,n,tot,prim[N],to[500],has[N],lis[N],ans;
bool vis[31];
vector <int> fac[N];
bool cmp(int a,int b) {
if (prim[a] != prim[b]) return prim[a] < prim[b];
return a / prim[a] < b / prim[b];
}
signed main() {
read(n,mod);
for (int i = 2;i <= n;++i) {
int x = i;
for (int j = 0;j < 8;++j) {
if (x % px[j] == 0) {
has[i] ^= (1 << j);
while (x % px[j] == 0) x /= px[j];
}
}
prim[i] = x;
lis[i] = i;
}
sort(lis + 2,lis + 1 + n,cmp);
f[1][0][0] = 1;
int cur = 1,lst = 0;
for (int i = 2;i <= n;++i) {
int x = lis[i];
//printf("%d %d\n",x,prim[x]);
cur ^= 1,lst ^= 1;
for (int s1 = 0;s1 < (1 << 8);++s1)
for (int s2 = 0;s2 < (1 << 8);++s2)
f[cur][s1][s2] = f[lst][s1][s2],w[cur][s1][s2] = w[lst][s1][s2],g[cur][s1][s2] = g[lst][s1][s2];
for (int s1 = 0;s1 < (1 << 8);++s1) {
for (int s2 = 0;s2 < (1 << 8);++s2) {
if (s1 & s2) continue;
if (prim[x] == 1) {//nobigprime
if ((s2 & has[x]) == 0) f[cur][s1 | has[x]][s2] = (f[cur][s1 | has[x]][s2] + f[lst][s1][s2]) % mod;
if ((s1 & has[x]) == 0) f[cur][s1][s2 | has[x]] = (f[cur][s1][s2 | has[x]] + f[lst][s1][s2]) % mod;
} else if (prim[x] != prim[lis[i - 1]]) {
f[cur][s1][s2] = (f[lst][s1][s2] + w[lst][s1][s2] + g[lst][s1][s2]) % mod;
g[cur][s1][s2] = w[cur][s1][s2] = f[cur][s1][s2];
} else {
if ((s2 & has[x]) == 0) g[cur][s1 | has[x]][s2] = ((g[cur][s1 | has[x]][s2] + g[lst][s1][s2]) % mod+ f[cur][s1][s2]) % mod;
if ((s1 & has[x]) == 0) w[cur][s1][s2 | has[x]] = ((w[cur][s1][s2 | has[x]] + w[lst][s1][s2]) % mod + f[cur][s1][s2]) % mod;
}
}
}
}
for (int s1 = 0;s1 < (1 << 8);++s1) {
for (int s2 = 0;s2 < (1 << 8);++s2) {
if ((s1 & s2) == 0) ans = (ans + (f[cur][s1][s2]+ g[cur][s1][s2]) % mod + w[cur][s1][s2]) % mod;
}
}
printf("%lld\n",ans);
return 0;
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。