[Codeforces Round #195 DIV2E (CF336E)] Vasily the Bear and Painting Square

题意

n=0 时有如下图像

n=1 时有如下图像

n=2 时有如下图像

题目要求对定的 n 的图像,染k个三角形(并不要求染最小的三角形),求染色方案数.
需要注意的是:即使染色完毕后结果图案相同,若某一步染的三角形不相同也算是不同的两种方案.

传送门

分析

可以发现染色是可以分层次的.
我们把n的图像分为 n 层,最内层为0,则可以一层一层向外染色.
如果想对小三角形染色,是与上一层及以内是否染过色无关的.
如果想对大三角形染色,我们只需要判断上一层及以内的一个染色方案中,某一象限内是否染过色即可.
我们画出图来分析

我们发现在划分为 n 层后层与层之间分为两层更加合适.
上图中从第i层到第 i+0.5 层的转移很简单,并不需要判断前 i 层之内某一象限是否被染过色(只有①,②,③,④可选)
重点是第i+0.5层到第 i+1
在这一层中我们可以选择
1. 像⑥,⑦,⑧这样的小三角形
2. 像⑤⑥,⑦⑧这样两个三角形拼接起来的中等大小的三角形
3. 像第一象限和第二象限拼在一起的超级大三角形

于是按照这个思路我们可以求得层与层之间的转移
在从 i=0 开始循环一边,用已经求得的转移来转移,就可以求到大小为 n 的图,染k个三角形的方案数.

那么,题目说若某一步染的三角形不相同也算是不同,其实答案乘以 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 ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值