您的位置:首页 > 其它

[BZOJ4197][UOJ129][Noi2015]寿司晚宴(状压DP)

2018-03-17 18:01 447 查看

一、分析

讲真,这题看上去一点都不像状压。

二、思路

显然,选了一个数,就相当于选了一个质因子集合。

可以设状态:f[i][S1][S2]f[i][S1][S2]:到了第ii个数,第一个集合选取质因子的状态为S1S1(S1S1中包含kk表示第一个集合中有质因子kk,否则没有质因子kk),第二个集合选取质因子的状态为S2S2,但这样时空复杂度都难以承受……

而此题的思路是:凡是大于n−−√n的质因子,在[2,n][2,n]内的每个数都只出现一个。

所以可以考虑只压缩{2,3,5,7,11,13,17,19}{2,3,5,7,11,13,17,19}这个集合,这样复杂度就可以承受了。

三、预处理

先把数按一定的规则进行排序:

给一个数定义一个关键码keyikeyi:数ii如果有大于1919的质因子,那么keyikeyi就等于这个质因子,否则keyi=1keyi=1。

然后把所有的数按照keykey排序。定义num[i]num[i]为排序后第ii个数。

然后分情况考虑:

四、如果第i+1i+1个数的关键码(keykey)等于11

(1)两个集合都不选num[i+1]num[i+1]:

f[i+1][S1][S2]+=f[i][S1][S2]f[i+1][S1][S2]+=f[i][S1][S2]

(2)第一个集合选num[i+1]num[i+1]:

(如果S2S2和prinum[i+1]prinum[i+1]没有交集,prixprix为xx的质因子集合)

f[i+1][S1|prinum[i+1]][S2]+=f[i][S1][S2]f[i+1][S1|prinum[i+1]][S2]+=f[i][S1][S2]

(3)第二个集合选num[i+1]num[i+1]:

(如果S1S1和prinum[i+1]prinum[i+1]没有交集)

f[i+1][S1][S2|prinum[i+1]]+=f[i][S1][S2]f[i+1][S1][S2|prinum[i+1]]+=f[i][S1][S2]

五、如果第i+1i+1个数的关键码不为11

下面设关键码为RR。

这时候要增设gg:

g[i][0][S1][S2]g[i][0][S1][S2]:到第ii个数,两个集合都没有质因子RR,两个集合状态为S1S1和S2S2。

g[i][1][S1][S2]g[i][1][S1][S2]:第一个集合包含质因子RR,同上。

g[i][2][S1][S2]g[i][2][S1][S2]:第二个集合包含质因子RR,同上。

具体地,从小到大,对11除外的每一种关键码进行DP(因为排序后关键码相同的数被分到了一个区间内)

设第ll个数到第rr个数的关键码为RR。那么先设:

g[l−1][0][S1][S2]=f[l−1][S1][S2]g[l−1][0][S1][S2]=f[l−1][S1][S2]

g[l−1][1][S1][S2]=g[l−1][2][S1][S2]=0g[l−1][1][S1][S2]=g[l−1][2][S1][S2]=0

同样与ff相似,但要注意质因子RR:

(1)不选num[i+1]num[i+1]:

g[i+1][a][S1][S2]+=g[i][a][S1][S2]g[i+1][a][S1][S2]+=g[i][a][S1][S2]

(2)选num[i+1]num[i+1]加入集合一:

(如果S2S2和prinum[i+1]prinum[i+1]没有交集)

g[i+1][1][S1|prinum[i+1]][S2]+=g[i][0][S1][S2]+g[i][1][S1][S2]g[i+1][1][S1|prinum[i+1]][S2]+=g[i][0][S1][S2]+g[i][1][S1][S2]

(3)选num[i+1]num[i+1]加入集合二:

(如果S1S1和prinum[i+1]prinum[i+1]没有交集)

g[i+1][2][S1][S2|prinum[i+1]]+=g[i][0][S1][S2]+g[i][2][S1][S2]g[i+1][2][S1][S2|prinum[i+1]]+=g[i][0][S1][S2]+g[i][2][S1][S2]

第一维DP到rr时就停止这一部分的DP,并令:

f[r][S1][S2]=∑a=02{g[r][a][S1][S2]}f[r][S1][S2]=∑a=02{g[r][a][S1][S2]}

六、Other

最后答案为∑f[n−1][S1][S2]∑f[n−1][S1][S2]。

使用滚动数组优化空间。

由于S2S2必须是S1S1相对全集{2,3,5,7,11,13,17,19}{2,3,5,7,11,13,17,19}的补集的子集,

所以复杂度O(38×n)O(38×n)。

七、代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int C = 256, N = 502, M = 105;
int n, m, pri[M], divi
;
bool mark
;
ll MX, f[2][C][C], g[2][3][C][C];
struct cyx
{
int x, d;
} orz
;
bool comp(cyx a, cyx b) {return a.d < b.d;}
int main()
{
int i;
cin >> n >> MX;
for (int i = 2; i <= 22; i++)
{
if (mark[i]) continue;
for (int j = i * i; j <= 500; j += i)
mark[j] = 1;
}
for (int i = 2; i <= 500; i++)
if (!mark[i]) pri[++m] = i;
for (int i = 2; i <= n; i++)
{
orz[i - 1].x = i; orz[i - 1].d = 1;
for (int j = 1; j <= 8; j++)
if (i % pri[j] == 0) divi[i] |= 1 << j - 1;
for (int j = 9; j <= m; j++)
if (i % pri[j] == 0) {orz[i - 1].d = j; break;}
}
sort(orz + 1, orz + n, comp);
f[0][0][0] = 1;
for (i = 0; orz[i + 1].d == 1; i++)
{
int o = i & 1;
for (int j = 0; j < 256; j++)
for (int k = 255 - j; ; k = (k - 1) & (255 - j))
{
f[o ^ 1][j][k] = 0;
if (k == 0) break;
}
for (int j = 0; j < 256; j++)
for (int k = 255 - j; ; k = (k - 1) & (255 - j))
{
int S1 = j | divi[orz[i + 1].x], S2 = k | (divi[orz[i + 1].x]);
f[o ^ 1][j][k] = (f[o ^ 1][j][k] + f[o][j][k]) % MX;
if (!(S1 & k)) f[o ^ 1][S1][k] = (f[o ^ 1][S1][k] + f[o][j][k]) % MX;
if (!(j & S2)) f[o ^ 1][j][S2] = (f[o ^ 1][j][S2] + f[o][j][k]) % MX;
if (k == 0) break;
}
}
for (; i < n - 1;)
{
int nxt = i + 1;
while (orz[nxt + 1].d == orz[i + 1].d) nxt++;
for (int j = 0; j < 256; j++)
for (int k = 255 - j; ; k = (k - 1) & (255 - j))
{
g[i & 1][0][j][k] = f[i & 1][j][k];
g[i & 1][1][j][k] = g[i & 1][2][j][k] = 0;
if (k == 0) break;
}
for (int h = i; h < nxt; h++)
{
int o = h & 1;
for (int j = 0; j < 256; j++)
for (int k = 255 - j; ; k = (k - 1) & (255 - j))
{
g[o ^ 1][0][j][k] = g[o ^ 1][1][j][k]
= g[o ^ 1][2][j][k] = 0;
if (k == 0) break;
}
for (int j = 0; j < 256; j++)
for (int k = 255 - j; ; k = (k - 1) & (255 - j))
{
int S1 = j | divi[orz[h + 1].x], S2 = k | (divi[orz[h + 1].x]);
g[o ^ 1][0][j][k] = (g[o ^ 1][0][j][k] + g[o][0][j][k]) % MX;
g[o ^ 1][1][j][k] = (g[o ^ 1][1][j][k] + g[o][1][j][k]) % MX;
g[o ^ 1][2][j][k] = (g[o ^ 1][2][j][k] + g[o][2][j][k]) % MX;
if (!(S1 & k))
g[o ^ 1][1][S1][k] = (g[o ^ 1][1][S1][k] + g[o][0][j][k]
+ g[o][1][j][k]) % MX;
if (!(j & S2))
g[o ^ 1][2][j][S2] = (g[o ^ 1][2][j][S2] + g[o][0][j][k]
+ g[o][2][j][k]) % MX;
if (k == 0) break;
}
}
for (int j = 0; j < 256; j++)
for (int k = 255 - j; ; k = (k - 1) & (255 - j))
{
f[nxt & 1][j][k] = (g[nxt & 1][0][j][k] +
g[nxt & 1][1][j][k] + g[nxt & 1][2][j][k]) % MX;
if (k == 0) break;
}
i = nxt;
}
ll ans = 0;
for (int j = 0; j < 256; j++)
for (int k = 255 - j; ; k = (k - 1) & (255 - j))
{
ans = (ans + f[n - 1 & 1][j][k]) % MX;
if (k == 0) break;
}
cout << ans << endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: