【BZOJ-2476】战场的数目 矩阵乘法 + 递推
2476: 战场的数目
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 58 Solved: 38
[Submit][Status][Discuss]
Description
Input
输入文件最多包含25组测试数据,每个数据仅包含一行,有一个整数p(1<=p<=109),表示战场的图形周长。p=0表示输入结束,你的程序不应当处理这一行。
Output
对于每组数据,输出仅一行,即满足条件的战场总数除以987654321的余数。
Sample Input
7
8
9
10
0
8
9
10
0
Sample Output
0
2
0
9
2
0
9
HINT
湖南省第六届大学生计算机程序设计竞赛
Source
Solution
战场的定义比较趋近于俄罗斯方块,及不能有悬空的块;每个块下面必须为底或者是另一个块,并且这些块不能构成一个矩形。
直接入手比较麻烦。
很显然的就是高就是最高的块所在的层数,宽也是最后一层的宽度。
分情况讨论一下,显然只有$p$为偶数的时候才可能有答案,奇数一定无解。
1.如果左/右只有一边存在一个单独的块。
这样删掉这个块将会得到周长为$p-2$时的一种方案。
2.如果左/右都不存在单独一个块。
这样无法通过删单个块得到一种周长为$p-2$的方案,但是,可以通过去掉最后一层得到一种周长为$p-2$的方案。
3.如果左/右都存在一个单独的块。
这种方案,会在情况1中额外统计,所以要减去,而这样的方案数则对应是周长为$p-4$时的方案。
然后就可以得到递推式$f[p]=2*f[p-2]+f[p-2]-f[p-4]=3*f[p-2]-f[p-4]$
然后这个显然是可以矩阵乘法优化的,不过这样会产生一些不必要的内存,所以我们把$p=p/2$然后就可以得到$f[p]=3*f[p-1]-f[p-2]$
这样得到的答案,是满足的情况,是包含正好组成矩形的情况的,所以我们在最后把它减去即可。
有一些小细节,稍微注意一下!
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; #define LL long long inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define P 987654321 struct Matrix{int a[4][4]; Matrix() {memset(a,0,sizeof(a));}}A,B; int N,T; Matrix operator * (Matrix A,Matrix B) { Matrix C; for (int k=1; k<=3; k++) for (int i=1; i<=3; i++) if (A.a[i][k]) for (int j=1; j<=3; j++) if (B.a[k][j]) C.a[i][j]=(C.a[i][j]+((LL)A.a[i][k]*B.a[k][j]+P)%P+P)%P; return C; } Matrix operator ^ (Matrix x,int k) { Matrix re; for (int i=1; i<=3; i++) re.a[i][i]=1; for (int i=k; i; i>>=1,x=x*x) if (i&1) re=re*x; return re; } int main() { A.a[1][1]=3; A.a[1][2]=-1; A.a[1][3]=0; A.a[2][1]=1; A.a[2][2]=0; A.a[2][3]=0; A.a[3][1]=0; A.a[3][2]=1; A.a[3][3]=0; B.a[1][1]=13; B.a[2][1]=5; B.a[3][1]=0; while (1) { N=read(); if (!N) break; if (N&1 || N<8) {puts("0"); continue;} if (N==8) {puts("2"); continue;} if (N==10) {puts("9"); continue;} Matrix ANS; ANS=A^(N/2-5); ANS=ANS*B; // for (int i=1; i<=3; i++,puts("")) // for (int j=1; j<=3; j++) // printf("%d ",ANS.a[i][j]); printf("%d\n",(ANS.a[1][1]-(N/2-1)+P)%P); } return 0; }
又发了道水题,很惭愧....
——It's a lonely path. Don't make it any lonelier than it has to be.