题目描述
为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴。小 G 和小 W 作为参加 NOI 的选手,也被邀请参加了寿司晚宴。
输入
输入文件的第 1 行包含 2 个正整数 n,p,中间用单个空格隔开,表示共有 n 种寿司,最终和谐的方案数要对 p 取模。
输出
输出一行包含 1 个整数,表示所求的方案模 p 的结果。
样例输入
3 10000
样例输出
9
题解
分解质因数+状态压缩dp
对于这种题肯定想到使用状压dp来解决,即设f[v1][v2]表示甲选质数的状态为v1,乙选质数的状态为v2的方案数。
但是这样状态数会爆炸。
考虑:大于$sqrt{500}$的质数在某数中最多只会出现一次,而小于等于$sqrt{500}$的质数只有8个。
所以我们只需要记录8个小质数的状态,对于大质数单独处理。
对于每个2~n的数,把它们分解质因数,并记录它们包含小质数的状态和包含的大质数(没有则为0)。
然后把每个数按照大质数的大小排序,这样每个大质数出现的位置是一段连续的区间。
对于每个大质数对应的区间,设$g[i][j]$表示乙不选这个大质数,甲、乙状态分别为i、j的方案数;$h[i][j]$表示甲不选这个大质数,甲、乙状态分别为i、j的方案数。
那么初始$g[i][j]=h[i][j]=f[i][j]$,转移时$g[i|v][j]+=g[i][j] (v&j=0)$,h同理。
更新完g和h后更新f为$f[i][j]=g[i][j]+h[i][j]-f[i][j]$,因为两者都不选的情况算了2次。
对于不包含大质数的数,拿出来单独处理即可。
注意一下循环顺序,不要重复更新,必要时可以记录原状态(不包含大质数的数的处理方法中,f数组可能在使用前已经更新,所以这里再使用g数组记录原来的f)
时间复杂度$O(500·2^{16})$
#include <cstdio> #include <cstring> #include <algorithm> #define N 520 using namespace std; const int pos[] = {0 , 0 , 0 , 1 , 0 , 2 , 0 , 3 , 0 , 0 , 0 , 4 , 0 , 5 , 0 , 0 , 0 , 6 , 0 , 7}; struct data { int val , mp; bool operator<(const data a)const {return mp > a.mp;} }a[N]; int f[N][N] , g[N][N] , h[N][N]; int main() { int n , p , i , j , k , l , last , t , ans = 0; scanf("%d%d" , &n , &p); for(i = 2 ; i <= n ; i ++ ) { for(j = 2 , t = i ; j * j <= t ; j ++ ) { if(t % j == 0) { a[i - 1].val |= (1 << pos[j]); while(t % j == 0) t /= j; } } if(t > 1) { if(t > 19) a[i - 1].mp = t; else a[i - 1].val |= (1 << pos[t]); } } sort(a + 1 , a + n); f[0][0] = 1; for(i = last = 1 ; i < n ; i = last + 1) { if(a[i].mp) { while(last < n - 1 && a[last + 1].mp == a[i].mp) last ++ ; for(j = 0 ; j < 256 ; j ++ ) for(k = 0 ; k < 256 ; k ++ ) g[j][k] = h[j][k] = f[j][k]; for(j = i ; j <= last ; j ++ ) for(k = 255 ; ~k ; k -- ) for(l = 255 ; ~l ; l -- ) if(!(k & l) && !(a[j].val & l)) g[k | a[j].val][l] = (g[k | a[j].val][l] + g[k][l]) % p , h[l][k | a[j].val] = (h[l][k | a[j].val] + h[l][k]) % p; for(j = 0 ; j < 256 ; j ++ ) for(k = 0 ; k < 256 ; k ++ ) f[j][k] = ((g[j][k] + h[j][k] - f[j][k]) % p + p) % p; } else { last = i; for(j = 0 ; j < 256 ; j ++ ) for(k = 0 ; k < 256 ; k ++ ) g[j][k] = f[j][k]; for(j = 255 ; ~j ; j -- ) for(k = 255 ; ~k ; k -- ) if(!(j & k) && !(a[i].val & k)) f[j | a[i].val][k] = (f[j | a[i].val][k] + g[j][k]) % p , f[k][j | a[i].val] = (f[k][j | a[i].val] + g[j][k]) % p; } } for(i = 0 ; i < 256 ; i ++ ) for(j = 0 ; j < 256 ; j ++ ) if(!(i & j) && f[i][j]) ans = (ans + f[i][j]) % p; printf("%d " , ans); return 0; }