传送门 : 寿司晚宴
【Description】:
给定 (n - 1) 个数 (a_i) , (a_i) 满足 (a_i = i + 1) , 将 (n - 1) 个数分成两堆,两堆必须满足互质,求解方案数。
- 对于 ( ext{30%})数据 满足 (n leq 30)
- 对于 ( ext{100%}) 数据 满足 (n leq 500)
【Solution】:
Part 1
对于 (30 ext{%}) 的数据 , 我们显然能够想到,我们状压 (G) 和 (W) 选择的质数集。
方案数合法的充要条件 :
(G) 所选的集合 (P) 和 (W) 选择 (W) 的集合满足 : (Gin S , W in T , S cap T = phi)
(S , T) 是两个大质数集。
我们发现 (n leq 30) 的时候,最多只有 (10) 个 质数。
为了表示整个局面和下一步的转移,我们设 (f_{i,G,W}) 为 选完 (i) 这个寿司,(G) 选择的质数集, (W) 选择的质数集 为 (G , W) ,显然我们对于状态转移,就是这个寿司是选择,就有状态转移 :
(P_i) 则是代表第 (i) 个寿司的美味度的质因数集。
Part 2 , 3
当 (n leq 200) 的时候,想不出来和 (nleq 500) 有什么不一样的做法。
Part 4
当 (n leq 500) 时 , 显然状压 (500) 以内的质因子集是不大现实,(500) 以内的质数个数是 (95) 个来着,状压个锤子 。
有一个结论可能会帮你完成该题
对于一个数 (n) 最多有一个 > (sqrt n) 的质因子。
那么我们就能够直接取出这一个质因子即可。 设为 (big) ,其他我们设为 (small) ,(sqrt 500) 大概取 (22) ,所以 我们求解 (small) 的时候,只需要将 (22) 以内的质数搞一下分解就好。
所有 (big) 相同的数显然只能放在一个集合中,所以按照 (big) 的大小排序,这样就可以把所有 (big) 相同的数放在一起算 。
在计算 (big) 相同的时候,我们发现,就和 (30 opts) 是一样的,我们设 (f_1[P][W] ,f_2[P][W]) 去转移 (30 opts) 的状态转移 。
小小的容斥原理 (f_{P,W}) 加了两遍
(f_{P,W} = f_1[P][W] + f_2[P][W] - f[P][W]) , 。
最后取答案 (ans = sum_{P}sum_{W} f_{P,W})
【Code】
/*
Author : Zmonarch
知识点 :代码实现看的第一篇题解。
*/
#include <bits/stdc++.h>
#define int long long
#define qwq register
#define qaq inline
#define inf 2147483647
using namespace std ;
const int kmaxn = 550 ;
const int kmod = 1e9 + 7 ;
const int eps = 1e-6 ;
const int num[] = {0 , 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19} ;
inline int read() {
int x = 0 , f = 1 ; char ch = getchar() ;
while(!isdigit(ch)) {if(ch == '-') f = - 1 ; ch = getchar() ;}
while( isdigit(ch)) {x = x * 10 + ch - '0' ; ch = getchar() ;}
return x * f ;
}
int n , mod , ans ;
int f[kmaxn][kmaxn] , f1[kmaxn][kmaxn] , f2[kmaxn][kmaxn] ;
struct node
{
int S = 0 , big , val ; //质数集合 , 大质数
void init()
{
int tmp = val ; big = 0 ;
for(int i = 1 ; i <= 8 ; i++) //分解质因数
{
if(tmp % num[i]) continue ;
S |= (1 << (i - 1)) ;
while((tmp % num[i]) == 0) tmp /= num[i] ;
}
if(tmp != 1) big = tmp ;
}
}a[kmaxn] ;
inline bool cmp(node a , node b) {return a.big < b.big ;}
signed main()
{
n = read() , mod = read() ;
for(qwq int i = 2 ; i <= n ; i++) a[i - 1].val = i , a[i - 1].init() ;
std::sort(a + 1 , a + n , cmp) ;f[0][0] = 1 ;
for(qwq int i = 1 ; i <= n - 1 ; i++)
{
if((i == 1) || (a[i].big != a[i - 1].big) || (!a[i].big))
{
memcpy(f1 , f , sizeof(f1)) ;
memcpy(f2 , f , sizeof(f2)) ;
}
for(int j=255; j>=0; j--)
for(int k=255; k>=0; k--)
{
if(j & k) continue ;
if(!(a[i].S & j)) f2[j][k | a[i].S] = (f2[j][k | a[i].S] + f2[j][k]) % mod ;
if(!(a[i].S & k)) f1[j | a[i].S][k] = (f1[j | a[i].S][k] + f1[j][k]) % mod;
}
if((i == n - 1) || (a[i].big != a[i + 1].big) || (!a[i].big))
{
for(int j=0; j<=255; j++)
for(int k=0; k<=255; k++)
{
if(j & k) continue ;
f[j][k] = ( (f1[j][k] + f2[j][k] ) % mod - f[j][k] + mod) % mod ;
}
}
}
ans = 0 ;
for(int j=0; j<=255; j++)
for(int k=0; k<=255; k++)
if(!(j & k)) ans = (ans + f[j][k]) % mod ;
printf("%lld
" , ans) ;
return 0 ;
}