2018牛客暑期多校第一场B(组合数学dp)

链接: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个点,方案数是C_{n-1}^{n-2}*f(n-2)

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

所以该情况方案数为C_{n-1}^{n-2}*f(n-2)=(n-1)*f(n-2)

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

圆排列方案数-百度百科:https://baike.baidu.com/item/%E5%9C%86%E6%8E%92%E5%88%97

所以该情况方案数为\sum_{k=2}^{n-3}C_{n-1}^{k}f(k)\frac{(n-1-k)!}{2}=\sum_{k=2}^{n-3}f(k)\frac{(n-1)!}{2*k!}

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

f(n)=(n-1)f(n-2)+\sum_{k=2}^{n-3}f(k)\frac{(n-1)!}{2*k!}                     记为

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

(n-1)f(n-1)=(n-1)(n-2)f(n-3)+(n-1)\sum_{k=2}^{n-4}f(k)\frac{(n-2)!}{2*k!}    即:

(n-1)f(n-1)=(n-1)(n-2)f(n-3)+\sum_{k=2}^{n-4}f(k)\frac{(n-1)!}{2*k!}           记为

-化简整理可得:

f(n)=(n-1)[f(n-1)+f(n-2)]-\frac{1}{2}(n-1)(n-2)f(n-3)

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

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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]);
    }
}

 

你可能感兴趣的:(dp,组合数学,dp,式子化简)