保险箱
问题描述
小 A 轻松地解开了密码锁,获得了神秘宝物(你们猜啊就不告诉你们是什么)。他将宝物藏在了自己和小 B 共同拥有的保险箱里。它有一套小 A 开发的通用而机智的身份认证系统,这套系统将利用传感器将每个人识别为一个不变的电子认证特征:一个 n*n 的矩阵。其中,矩阵的每行每列都有两个点。这一身份认证系统会利用玄学算法求出这一矩阵,并且对此进行解析。由于每个人的特征都是独一无二的矩阵,系统可以快速地认证出身份,简单、快
捷、方便。
但是小 A 显然不希望别人和他破解密码锁那样轻松地解开这一身份认证系统,于是他不断地修改着自己做识别的玄学算法,使其更加完备。他发现对于一个矩阵,只要其他人识别出的特征矩阵不与之“相似”,身份认证信息的错误率会降低很多,也更不容易被欺骗。经过多次实验,他发现 a 与 b“相似”意味着:矩阵 a 通过任意次行列变换可以变成矩阵 b。
小 A 想知道对于一个 n,可以有多少种不“重复”的 n*n 矩阵。为了让题目更水,你只需要输出答案 mod 10 8 +7 的值就可以了。
输入
输入文件名为 safe.in。
第一行,一个整数 T。表示数据组数。
接下来 T 行,每行一个整数 n。表示一组数据。
输出
输出文件名为 safe.out。
T 行,每行一个整数,表示方案数。由于答案可能很大,只需要输出 mod 10 8 +7 的值。
Sample Input
3
2
3
4
Sample Output
1
1
2
样例解释
属于同一种方案。
数据范围
对于 10%的数据,N≤5。
对于 50%的数据,N≤150。
对于 100%的数据,T≤5,N≤2×10^3 。
题解
10%的数据范围,我们可以采用打表的形式求解,题目样例中已经给出了大部分0~5的答案,这算是一种快速得分的办法。
对于50%~100%的数据范围,需要将题目联想成一张二分图。二分图的两侧分别为图中点的X、Y轴,二分图中的连线则代表了一个图中的点。联想到二分图之后,我们来观察本题的性质:矩阵的每行每列都有两个点。这个地方可以告诉我们,在本题里,转换成二分图之后,二分图里至少会有一个环(证明:若二分图中无环,则该二分图的长度将会变成无限长),而多少个环,以及环的大小,就是这个二分图的种类。图中行、列变换是可以理解为在环内的数字移动而已,环中的连线不会改变,那由此想出的解法是自然数拆分。
而自然数拆分又要考虑一些边界情况,环不能是由一组X、Y组成,必定至少由两组X、Y组成,所以自然数不能拆分成含1的情况,那这里由动态转移方程得出
附上Pascal代码:
program safe;
var f:Array[0..5000,0..5000] of longint;
ck:array[0..5000,0..5000] of boolean;
i,j,t,n,k:longint;
const mo=100000007;
function cal(u,v:longint):longint;
var tmp:longint;
begin
if (ck[u,v]) then exit(f[u,v]);
ck[u,v]:=true;
if (u=1) or (v = 1) then begin f[u,v]:=0;exit(f[u,v]);end;
if u=0 then begin f[u,v]:=1;exit(1);end;
if v=2 then
begin
tmp:=(u mod 2);
if tmp=0 then f[u,v]:=1 else f[u,v]:=0;
exit(f[u,v]);end;
if u<v then begin f[u,v]:=cal(u,u);exit(f[u,v]);end;
f[u,v]:=(cal(u-v,v) + cal(u,v-1)) mod mo;
exit(f[u,v]);
end;
begin
assign(input,'safe.in') ;reset(input);
assign(output,'safe.out');rewrite(output);
read(t);
for i:=1 to t do
begin
fillchar(ck,sizeof(ck),false);
read(n);
writeln(cal(n,n));
end;
close(input);close(output);
end.