链接:https://www.nowcoder.com/acm/contest/139/B
来源:牛客网
题目描述
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.
示例1
输入
复制
3 1000000000 100000 1000000000
输出
复制
1 507109376
备注:
* 1 ≤ n ≤ 105 * 1 ≤ m ≤ 109 * The sum of n does not exceed 107.
这题其实我一开始就看出来是看作图来做,度都为二,最后整个图都是形成环,然而下面就不会做了。
其实没想到给的条件不多,没有特殊情况,很可能就是 dp,当时推导dp转移方程的时候没有认真推,一直在想用强连通分量的方法解决。
其实这个题目用dp,转移方程没有想象中的难推。
用f(n)表示n个节点的情况。
那么f(n)可以分为两种情况。
- 取前n-1中的任意一个点与其形成一个二元环,其余点的构成情况有f(n-2)种,这种情况下,不相互干扰,所以有(n-1)*f(n-2)种构成方式。
- 保留前n-1个点中的任意k个点的构成方式(f(k)),其余点和新点构成环。(也可以是取,但是保留的话表示比较方便),此时对于k个点,有f(k)种可能,对于构成环有(n-1)(n-2)..(n-k)/2种可能(/2是因为环顺时针逆时针顺序一样可以视为同一个环,邻接矩阵相同,1-2-3-1和1-3-2-1这两个环其实是同一个。),所以有(n-1)!f(k)/k!/2种可能,枚举每个可能的k值便可。0<=k<n-2
对于第二种情况,f(N)中含有sigma,用f(n)-(n-1)f(n-1)可以消去sigma。
最后得到f(n)=(n-1)*f(n-1)+(n-1)*f(n-2)-(n-1)*(n-2)/2*f(n-3)
用这个式子dp即可。
附AC代码
#include <bits/stdc++.h>
#define me0(x) memset(x,0,sizeof(x))
#define ll long long
#define ull unsigned long long
#define scan(x) scanf("%d",&x)
#define scanll(x) scanf("%lld",&x)
#define dscan(x,y) scanf("%d%d",&x,&y)
#define rep(x,be,en) for (x=be;x<=en;x++)
#define fr1(n) for (int i=1;i<=n;i++)
#define fr(n) for (int i=0;i<n;i++)
#define MOD 1e9+7;
using namespace std;
int n;
ll m;
const int maxn=1e5+10;
void ioquick()
{
cout.precision(15);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
}
ll f[maxn];
int main()
{
int i,j,k;
f[0]=0;
f[1]=0;
f[2]=1;
f[3]=1;
while (scanf("%d%lld",&n,&m)==2)
{
for (int i=4;i<=n;i++)
{
f[i]=(1ll*(i-1)*f[i-1])+(1ll*(i-1)*f[i-2])-(1ll*(i-1)*(i-2)/2*f[i-3]);
f[i]%=m;
}
while (f[n]<0) f[n]+=m;
printf("%lld\n",f[n]);
}
return 0;
}