链接:https://www.nowcoder.com/acm/contest/139/B

题面:Symmetric Matrix

Count the number of n x n matrices A satisfying the following condition modulo m.

* Ai, j ∈ {0, 1, 2} for all 1 ≤ i, j ≤ n.

* Ai, j = Aj, i for all 1 ≤ i, j ≤ n.

* Ai, 1 + Ai, 2 + ... + Ai, n = 2 for all 1 ≤ i ≤ n.

* A1, 1 = A2, 2 = ... = An, 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

备注:

* 1 ≤ n ≤ 105

* 1 ≤ m ≤ 109

* The sum of n does not exceed 107.

目描述:

在一个N*N的矩阵中填入0、1、2,对角线只能填0,要求是对称矩阵,并且每一行的数加和都为2

给出N,问满足以上要求的矩阵有多少个。

可将矩阵看成一个无向图,那么这个要求就变成了无向图中每个点的度都为2,可以重边,不能有自环。

所以这个无向图就是若干个多元环(包括2元环)。看成求n个点形成若干个环的方案数

用f(n)表示满足要求的矩阵个数,显然f(1)=0,f(2)=1,f(3)=1.

当n大于3时,考虑一般情况的f(n):

图中已经有n-1个点,现在要加入第n个点(以下称为新点)。

拿开n-2个点,方案数是2018牛客暑期多校第一场B(组合数学dp)_#include

剩下一个点与新点组成二元环,只有一种方案。

所以该情况方案数为2018牛客暑期多校第一场B(组合数学dp)_式子化简_02

拿开k个点(2<=k<=n-3),方案数是2018牛客暑期多校第一场B(组合数学dp)_式子化简_03

剩下n-1-k个点与新点组成n-k元环,n-k个球的顺时针环排列方案数为 (n-1-k)!,在无向图中,环的顺时针与逆时针同一个排列是同一个图(123顺时针串联和逆时针串联是同一个图),所以n-k个点的环排列方案数为(n-1-k)!/2

圆排列方案数-百度百科:​​

所以该情况方案数为2018牛客暑期多校第一场B(组合数学dp)_#define_04

总结:由以上①和②可得:

2018牛客暑期多校第一场B(组合数学dp)_无向图_05                     记为

式中令n=n-1,并左右同时乘以n-1得:

2018牛客暑期多校第一场B(组合数学dp)_#include_06    即:

2018牛客暑期多校第一场B(组合数学dp)_式子化简_07           记为

-化简整理可得:

2018牛客暑期多校第一场B(组合数学dp)_式子化简_08

O(n)递推即可得出答案

代码:

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<string>
#include<math.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>
#define INF 0x3f3f3f3f//3f3f3f3f
#define ll long long
#define ull unsigned long long
const long long MOD = 1000000007;
#define lowbit(a) ((a)&(-(a)))
using namespace std;
ll n, m;
ll f[100005];
int main(){
while(~scanf("%lld %lld",&n,&m)){
f[1]=0;f[2]=f[3]=1%m;
for(ll i=4;i<=n;i++){//注意i要用ll,或者在每个int开头的运算前要乘以1LL
f[i]=((i-1)*(f[i-1]+f[i-2])-(((i-1)*(i-2)/2)%m)*f[i-3]%m+m)%m;
}
printf("%lld\n",f[n]);
}
}