题意
当
n=0
时有如下图像
当
n=1
时有如下图像
当
n=2
时有如下图像
题目要求对定的
n
的图像,染k个三角形(并不要求染最小的三角形),求染色方案数.
需要注意的是:即使染色完毕后结果图案相同,若某一步染的三角形不相同也算是不同的两种方案.
分析
可以发现染色是可以分层次的.
我们把
如果想对小三角形染色,是与上一层及以内是否染过色无关的.
如果想对大三角形染色,我们只需要判断上一层及以内的一个染色方案中,某一象限内是否染过色即可.
我们画出图来分析
我们发现在划分为
n
层后层与层之间分为两层更加合适.
上图中从第
重点是第
在这一层中我们可以选择
1. 像⑥,⑦,⑧这样的小三角形
2. 像⑤⑥,⑦⑧这样两个三角形拼接起来的中等大小的三角形
3. 像第一象限和第二象限拼在一起的超级大三角形
于是按照这个思路我们可以求得层与层之间的转移
在从
i=0
开始循环一边,用已经求得的转移来转移,就可以求到大小为
n
的图,染
那么,题目说若某一步染的三角形不相同也算是不同
,其实答案乘以
k!
就好了么.
代码
#include <cstdio>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
int CH , NEG ;
template <typename TP>
inline void read(TP& ret) {
ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
if (CH == '-') NEG = true , CH = getchar() ;
while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
if (NEG) ret = -ret ;
}
template <typename TP>
inline void reads(TP& ret) {
while (ret=getchar() , ret<'!') ;
while (CH=getchar() , CH>'!') ;
}
#include <cstring>
const int ansmod = 1000000007 ;
#define inc(a,b) {a+=b;if(a>=ansmod)a-=ansmod;}
#define maxn 210LL//n
#define maxk 210LL//k
#define maxs 16LL//四位01状态
#define maxq 5LL//四个象限
#define maxt 10LL//一次转移最多增加8个三角形
int N , K ;
int g[16][10][16] = {0} ;
int h[5][10][2][2][16] = {0} ;
int f[401][210][16] = {0} ;
int num[16] ;
//我们把第i层到第i+1层拆为两个步骤
//即从第i层转移到第i+0.5层(立放到平放)和第i+0.5层到i+1层(平放到立放)
//g[原始状态][增加几个三角形][到达目标状态]的方案数
//h[前i个象限][增加了几个三角形][第i*2个三角形是否使用][第1个三角形是否使用][此时的状态]
//(第i*2个也就是前i个象限中的最后一个三角形)
//h是用来求g的
//求得g后可以用来求f
//最后要乘k的阶乘,因为染色方案结果相同,顺序不同也算不同
int i , j , k , A , B , use , last , first , now1 , now2 , cnt , can , Time , ans ;
int col[4] = {0} ;
int main() {
// #define READ
#ifdef READ
freopen(".in" ,"r",stdin ) ;
freopen(".out","w",stdout) ;
#endif
Rep (A,0,16-1) {
num[B=A] = 0 ;
while (B) num[A]+=(B&1) , B>>=1 ;
}
Rep (A,0,16-1) {
memset(h , 0 , sizeof h) ;
h[1][0][0][0][A]=h[1][1][1][0][A|1]=h[1][2][1][1][A|1]=h[1][1][0][1][A|1]=1;
if (!(A&1)) h[1][1][1][1][A|1]=1;
Rep (i,1,3) Rep (use,0,8) Rep (last,0,1) Rep (first,0,1)
Rep (B,0,16-1) if (h[i][use][last][first][B])
Rep (now1,0,1) Rep (now2,0,1) {
if (!now1 && !now2 && !((B>>i)&1))//当前这个象限不使用三角形且这个象限本来就是空的
inc(h[i+1][use+1][1][first][B|(1<<i)],h[i][use][last][first][B]);//那就添上一个大三角形
inc(h[i+1][use+now1+now2][now2][first][B|((now1|now2)<<i)],h[i][use][last][first][B]) ;
if (!last && !now1)
inc(h[i+1][use+1+now2][now2][first][B|(1<<i)|(1<<(i-1))],h[i][use][last][first][B]);
//与上一个象限的三角形拼接成一个三角形添入
if (!now1 && !now2 && !((B>>i)&1) && !((B>>(i-1))&1))
inc(h[i+1][use+1][1][first|(i==1)][B|(1<<i)|(1<<(i-1))],h[i][use][last][first][B]);
//如果两个相邻的象限都是空的,可以填一个很大的三角形 = =
}
Rep (use,0,8) Rep (B,0,16-1)
inc(h[4][use+1][1][1][B|9],h[4][use][0][0][B]) ;
Rep (use,0,8) Rep (B,0,16-1) if (!(B&1)&&!(B&8))
inc(h[4][use+1][1][1][B|9],h[4][use][0][0][B]) ;
Rep (use,0,8) Rep (last,0,1) Rep (first,0,1) Rep (B,0,16-1)
inc(g[A][use][B],h[4][use][last][first][B]) ;
}
Rep (A,0,16-1) Rep(B,0,16-1) {
col[0]=col[1]=col[2]=col[3]=0;
cnt = 0 ;
Rep (i , 0 , 4-1)
col[i] = ((A>>i)&1) , cnt += col[i] ;
can = true ;
Rep (i,0,4-1) if ((B>>i)&1) {
if (col[i] || col[(i+1)%4]) can = false ;
col[i] = col[(i+1)%4] = true ;
cnt ++ ;
}
if (!can) continue ;
K = 0 ;
Rev (i,4-1,0)
K = (K<<1)|col[i] ;
f[0][cnt][K] ++ ;
}
Rep (i,0,400-1) Rep (j,0,200) Rep (A,0,16-1)
if (f[i][j][A])
if (i&1) {
//从奇数转移到偶数,统计的形状是立起来的正方形
Rep (k,0,8)
//所以这里最多有八个小三角形
//但在转移时应该包括了两个三角形拼起来的情况
Rep (B,0,16-1)
inc(f[i+1][j+k][B],(long long)f[i][j][A]*g[A][k][B]%ansmod) ;
} else {
//从偶数转移到奇数,统计的形状是平方的正方形
Rep (k,0,16-1)
//所以只用判断四个角是否有三角形即可
inc(f[i+1][j+num[k]][A|k],f[i][j][A]) ;
}
read(Time) ;
while (Time --> 0) {
read(N) , read(K) ;
int ans = 0 ;
Rep (A,0,16-1)
inc(ans,f[2*N][K][A]) ;
Rep (i,1,K)
ans = (long long)ans*i%ansmod ;
printf("%d\n", ans) ;
}
#ifdef READ
fclose(stdin) ; fclose(stdout) ;
#else
getchar() ; getchar() ;
#endif
return 0 ;
}