哇很好很好的 Dp 题目
YYR 讲了这道题之后我瞬间就忘记啦,所以只能自己想了
大概思路还是记得哒
很明显这题就是不能让你去暴力枚举的,阶乘级别的复杂度啊
但是怎么去 Dp 呢,单纯的根据题意去尝试定义 Dp 方程似乎并不是很容易定义,这个时候需要尝试构造模型:
选了一个元素就不能选另一个元素或者另外某些元素的模型是什么呢?
是不是有树形 Dp 类似的题目:【没有上司的舞会】,选了上司就不能选择员工啊
但是无法将这个题目抽象成树形 Dp,因为一个合法的小于 N 的数 X 有两个爸爸:X/2 和 X/3,所以无法抽象成树规!
还有什么模型呢?
洛谷的【互不侵犯 King】,放置了一个 King 之后其周围的 8 个格子都不能放置 King 了,那么我们如果构造一个矩阵,横向相邻的数的商为3,纵向的商为2,如下:
1 3 9 ……
2 6 18 ……
4 12 36 ……
... ... ...
这样就和【互不侵犯 King】大相径庭……呸,大同小异啦(Splay那篇文章的梗哈哈哈哈):选择了一个数就不能选择上下左右相邻的数啦!
进一步发现,每一行最多有 列啦,这就很很很明显了,状压!
定义 Dp 方程 dp [ i ] [ { 当前行所有合法状态 } ] += dp [ i - 1 ] [ { 所有合法的上一行状态 } ]
一个状态是合法的当且仅当它两两相邻的位 & 值为 0!上下 & 的值为 1!
但是这样的话有些数是不会出现在上图的矩阵中的,哪些数呢?
当然是同时与 2、3 互质的数啦!
我们只需要预处理出所有与 2、3 互质的所有数,然后将每一个作为矩阵的左上角构造就好啦!
AC代码:
# include <bits/stdc++.h>
const int N = 23 , M = 17 , mod = 1000000001 ;
long long dp [ N ] [ ( 1 << M ) + 5 ] , to [ ( 1 << M ) + 5 ] ;
int tab [ ( 1 << M ) + 5 ] , num [ N ] , cnt [ N ] , sta [ ( 1 << M ) + 5 ] , buck [ ( 1 << M ) + 5 ] , tp [ ( 1 << M ) + 5 ];
int n , row , col , tot ;
long long Gyx = 1 ;
void init ( ) {
for ( int s1 = 0 ; s1 < ( 1 << M ) ; s1 ++ ) {
int tmp = s1 , dig = 0 ;
while ( tmp ) {
dig ++ ;
tmp >>= 1 ;
}
if ( ( s1 & ( s1 >> 1 ) ) || ( ( s1 & ( s1 << 1 ) ) ) ) {
to [ s1 ] = tot ;
buck [ s1 ] = dig ;
continue ;
}
sta [ ++ tot ] = s1 ;
to [ s1 ] = tot ;
buck [ s1 ] = dig ;
}
for ( int i = 1 ; i <= 11 ; i ++ ) {
int s = ( 1 << i ) - 1 ;
num [ buck [ s ] ] = to [ s ] ;
}
}
void get_tab ( ) {
for ( int i = 1 ; i <= n ; i ++ )
if ( ( i % 2 ) && ( i % 3 ) )
tab [ ++ tab [ 0 ] ] = i ;
}
void construct ( int x ) {
row = col = 0 ;
memset ( cnt , 0 , sizeof ( cnt ) ) ;
for ( int i = tab [ x ] ; i <= n ; i *= 2 ) {
row ++ ;
for ( int j = i ; j <= n ; j *= 3 )
cnt [ row ] ++ ;
}
}
long long Dp ( ) {
for ( int i = 1 ; i <= num [ cnt [ 1 ] ] ; i ++ )
dp [ 1 ] [ sta [ i ] ] = 1 ;
for ( int i = 2 ; i <= row ; i ++ )
for ( int j = 1 ; j <= num [ cnt [ i ] ] ; j ++ ) {
dp [ i ] [ sta [ j ] ] = 0 ;
for ( int k = 1 ; k <= num [ cnt [ i - 1 ] ] ; k ++ )
if ( ! ( sta [ k ] & sta [ j ] ) ) {
dp [ i ] [ sta [ j ] ] = ( dp [ i ] [ sta [ j ] ] + dp [ i - 1 ] [ sta [ k ] ] ) % mod ;
}
}
long long ans = 0 ;
for ( int i = 1 ; i <= num [ cnt [ row ] ] ; i ++ )
ans = ( ans + dp [ row ] [ sta [ i ] ] ) % mod ;
return ans ;
}
int main ( ) {
scanf ( "%d" , & n ) ;
get_tab ( ) ;
init ( ) ;
for ( int mmy = 1 ; mmy <= tab [ 0 ] ; mmy ++ ) {
construct ( mmy ) ;
int tmp = Dp ( ) ;
Gyx = ( int ) ( ( 1ll * Gyx * tmp ) % mod ) ;
}
printf ( "%lld" , Gyx ) ;
return 0 ;
}