时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
题目描述
Count the number of n x n matrices A satisfying the following condition modulo m.
* A i, j ∈ {0, 1, 2} for all 1 ≤ i, j ≤ n.
* A i, j = A j, i for all 1 ≤ i, j ≤ n.
* A i, 1 + A i, 2 + … + A i, n = 2 for all 1 ≤ i ≤ n.
* A 1, 1 = A 2, 2 = … = A n, n = 0.
* A i, j ∈ {0, 1, 2} for all 1 ≤ i, j ≤ n.
* A i, j = A j, i for all 1 ≤ i, j ≤ n.
* A i, 1 + A i, 2 + … + A i, n = 2 for all 1 ≤ i ≤ n.
* A 1, 1 = A 2, 2 = … = A n, n = 0.
输入描述:
The input consists of several test cases and is terminated by end-of-file.
Each test case contains two integers n and m.
输出描述:
For each test case, print an integer which denotes the result.
输入例子:
3 1000000000 100000 1000000000
输出例子:
1 507109376
PS:这次比赛一题滚粗 受到了深深地打击。。。。。。。
题意: 给出一个矩阵 矩阵每行的和必须为2 且是一个沿右对角线对称的矩阵 问你大小为n的这样的合法矩阵有多少个。
解题思路:
题目给出的合法矩阵是一个类似与邻接矩阵的样式。 所以应该往这方面去考虑。
每行之和等于2 , 代表每个点都连有两条边,可以有重边 不能有自环。
这说明 每个点属于且仅属于一个环。
因为输入只有一个n
应该要往dp递推的方向上去想。
现在开始找递推式。
定义dp[n]表示n个点构成的合法图的方案数。
思考每加入一个新球,如何从已知状态转移。
考虑从前面的n-1个球中选取一些球和新球组成一个环。
特殊考虑只取一个旧球的情况,
这种情况下这个旧球有n-1种方案 剩下的n-2个球组成的合法方案数已经求出。
所以这种情况下 方案数为(n-1)f(n-1)
推广到一般情况
当我们取k个旧球与新球组成环时,旧球的取法有C(n-1,k) 取出的旧球与新球组成环的方案数有(n-1-k)!种
但是考虑对称性 需要除以2。 又考虑到只取一个球的时候不需要考虑对称性 ,所以把这种情况单独摘出来考虑。
最后得到 dp[n]的递推式就是
dp[n] = (n-1) dp[n-2] + sigma(x:2->n-3)((n-1)!/(2*x!)dp[x])
但是这个东西有一个讨厌的sigma 我们可以通过相减的方法来消除这个sigma。
化简过程如下
感觉智商受到了成吨的打击
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
typedef long long LL;
const int MAX=1e5+19;
int MOD;
long long dp[MAX];
int main() {
int n;
dp[2]=dp[3]=1;
while(cin>>n>>MOD){
for(long long i=4;i<=n;i++){
dp[i]=(((i-1)*(dp[i-1]+dp[i-2]))%MOD-((i-1)*(i-2)/2*dp[i-3])%MOD+MOD)%MOD;
}
cout<<dp[n]<<endl;
}
return 0;
}