【JZOJ B组】【NOIP2015模拟10.27】魔法阵

Description

帕秋莉·诺蕾姬,有着“不动的大图书馆” 的称号,擅长使用各种各样的属性魔法。
——《东方求闻史记》
一如既往地,帕秋莉在图书馆中研究着魔法。今天,她在研究一本魔法书中的法阵。
这个法阵可以看成是按下面的规则生成一个规模为n(n 为非负整数) 的图形:
1. 在直角坐标系xOy 中,画4 条线段:[(0,0), (2^n,0)], [(0, 0), (��-2^n, 0)],[(0, 0), (0, 2^n)], [(0, 0), (0,-��2^n)]。
2. 对于所有的i = 1, 2, ……, n,画两个正方形,一个以(0, 2^i), (0,-��2^i),(2^i, 0), (��-2^i, 0) 为顶点,另一个以(��-2i-��1,-2i-��1),(��-2i-��1,2i-��1), (��2i-��1,-2i-��1)(��2i-��1,2i-��1) 为顶点。
3. 画一个以(1, 0), (��-1, 0), (0,-1), (0, 1) 为顶点的正方形。
比如,当n = 0 时的法阵是这样的:

当n = 2 时的法阵是这样的:

帕秋莉已经画出了这个巨大的法阵, 并且她能够同时控制k 种不同的属性元素。接下来,她要将这k 种元素分别依次灌注进法阵中的k 个不重叠不相交的三角形中,即这k 个三角形要灌注进不同的元素。这样,这个法阵就会被激活。
然而,灌注的方法有许多种。为了能最大限度地进行实验,帕秋莉想把所有的方法都实验一遍。而摆在她面前的问题是,总共有多少种不同的灌注方法呢?
注意,由于是法阵,看起来是中心对称或轴对称的两个三角形,也是实质不等同的。或者说,假如两个方案A 和B,只有通过旋转、翻转或二者组合才能使所有相同位置的三角形被灌注的元素相同,A 和B 仍然是不同的方案。只有本来所有相同位置的三角形被灌注的元素相同才算是相同的方案。
由于方法数会很大,帕秋莉只需要你输出模1,000,000,007(10^9 + 7) 的结果就可以了。

Input

输入文件magic.in。
一共一行两个整数n,k,意义如题中所述。

Output

输出文件magic.out。
一共一行一个整数res,表示灌注方案数模1,000,000,007(109 + 7) 得到的结果。

Sample Input

输入1:
0 0
输入2:
0 1
输入3:
0 2
输入4:
1 1

Sample Output

输出1:
1
输出2:
8
输出3:
32
输出4:
32

Data Constraint

对于15% 的数据,k <= 1。
对于30% 的数据,k <= 2。
另外,有15% 的数据,满足n <= 2。
对于100% 的数据,0<= n <= 200,0<= k <= min(200, 8n + 4)。

思路

普通解法

这里使用颜色的顺序对答案也会有影响:取出 k 个互不重叠的三角形之后,还会有 k! 中不同的上色方法。因此先不考虑颜色,只考虑取出 k 个互不重叠的三角形的方案数。
我们可以观察到,对于这样的一个 n = p 时的图形,这个图形中的三角形可以做如下分类:
1. 经过 (0, 0) 的三角形 (称为源三角形)
2. 不经过 (0, 0) 的三角形
单体三角形(称为一级三角形)
由两个单体三角形拼成的三角形(称为二级三角形)

当n=0时

这里写图片描述

总共有10种

现在来考虑对于任意一个 n = p 时的图形,通过在这个图形外加一层的方式来获得 n = p + 1 的图形。

此时我们可以干三种事:

  1. 给一个三角形加一层
  2. 选一些一级三角形
  3. 选一些二级三角形

这三种操作之间是会相互影响的,也就是说是要作为一次决策来进行的。根据它们本身的限制和它们之间相互影响的关系,规定这样的枚举顺序:

  1. 枚举将哪些能够扩展的源三角形向外扩展。注意,由于扩展不同的源三角形可能会造成不同的影响,因此这里枚举的是源三角形的集合。
  2. 在之前的基础上,枚举选取几个二级三角形。
  3. 在之前的基础上,枚举选取几个一级三角形。

可以看出,这样的一次决策会对以下变量产生影响:

  1. 已选取的三角形总数 t。
  2. 能继续扩展的源三角形集合 s。这里我们可以发现,对于某一个源三角形,如果在 n = p 的图形中不扩展这个三角形,在更大规模的图形中是无法对它再行扩展的。

为了描述 s,就要发掘它的性质。可以看到,s 是与初始 (n = 0) 时所选的方案有关的。因此,对于初始的 10 个方案,在每个方案中对被选取的三角形进行编号

这样,只要用初始方案编号 k 和这个方案中所有被选择的三角形标号的子集l,就能表示 s 了。
至此,如何表示目标函数已经清楚了。

但这样会TLE

优化解法

通过观察可以发现,对于任意一个 n = p 的图形来说,只考虑能够继续扩展的源三角形组成的形状,忽略所有不能继续扩展的源三角形、一级三角形和二级三角形以及所有能继续扩展的源三角形的内部结构的话,一定与上面 10 种方案中的某一种同构!
这表明,只要用方案的编号,就能将能继续扩展的源三角形的集合描述出来了。也就是说,这一对极端情况下所有方案的描述,能够扩展成对于任意一个n = p 都适用的描述。
因此,想要描述之前的 s(能够继续扩展的源三角形的集合), 只需使用 s 中所有的源三角形组成的形状的编号 label 来表示即可。

于是,新的目标函数就变成了:

f[i][j][label] 表示到第 i 层(即 n = i 的图形)为止,总共选取了 j 个三角形,
能继续扩展的源三角形组成的形状的编号为 label 时选取三角形的方案数。

  1. 枚举扩展之后的源三角形所组成形状的编号 label′。
  2. 枚举选取的二级三角形个数 s2。
  3. 枚举选取的一级三角形个数 s1。

这里需要添加一些辅助函数来帮助写出目标函数 f[i][j][label] 的转移方程。添加一个辅助函数 change[label1][label2], 表示从编号为 label1 的形状变成 label2 的形状有多少种方法。
添加两个辅助函数 tag1[label],tag2[label],分别表示在任意一个 n = p(p > 0)的图形中,能继续扩展的源三角形组成的形状的编号为 label,剩下的一级三角形和二级三角形的个数。

转移详见代码

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=277,mod=1000000007;
int n,k;
long long C[15][15],f[maxn][maxn][10],ass;
int ch[10][10]={
    {1,0,0,0,0,0,0,0,0,0},
    {1,1,0,0,0,0,0,0,0,0},
    {1,0,1,0,0,0,0,0,0,0},
    {1,2,0,1,0,0,0,0,0,0},
    {1,2,0,0,1,0,0,0,0,0},
    {1,1,1,0,0,1,0,0,0,0},
    {1,0,2,0,0,0,1,0,0,0},
    {1,3,0,2,1,0,0,1,0,0},
    {1,2,1,1,0,2,0,0,1,0},
    {1,4,0,4,2,0,0,4,0,1}
};
int tag1[10]={12,9,6,6,6,3,0,3,0,0},tag2[10]={4,2,1,1,0,0,0,0,0,0};
int main()
{
    scanf("%d%d",&n,&k);
    C[0][0]=1;
    for(int i=1; i<15; i++)
    {
        C[i][0]=1;
        for(int j=1; j<=i; j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    }
    f[0][0][0]=1;
    f[0][1][1]=4; f[0][1][2]=4;
    f[0][2][3]=4; f[0][2][4]=2; f[0][2][5]=8; f[0][2][6]=2;
    f[0][3][7]=4; f[0][3][8]=4;
    f[0][4][9]=1;//边界
    for(int i=0; i<n; i++)
        for(int j=0; j<10; j++)
            for(int p=0; p<=k; p++) if(f[i][p][j])
            {
                for(int q=0; q<10; q++) if(ch[j][q])
                {
                    int left1=tag1[q],left2=tag2[q];
//                  printf("left1=%d left2=%d\n",left1,left2);
                    for(int r=0; p+r<=k && r<=left2; r++)
                        for(int t=0; p+r+t<=k && t<=left1-2*r; t++)
                        {
                            (f[i+1][p+r+t][q]+=(f[i][p][j]*C[left2][r])%mod*C[left1-2*r][t]*ch[j][q])%=mod;
                        }
                }
            }
    for(int j=0; j<10; j++) (ass+=f[n][k][j])%=mod;
    for(int i=1; i<=k; i++) (ass*=i)%=mod;
    printf("%lld\n",ass);
}

猜你喜欢

转载自blog.csdn.net/eric1561759334/article/details/81021479
今日推荐