A: Monotonic Matrix Gym - 247727A
前置知识:
LGV 引理:
仅适用于 有向无环图。
这道题目因为从左上到右下只能是递增,即:靠右靠下位置的值总是大于等于靠左靠上位置的值,这样我们可以从右上到坐下画两条分解线,一条是 0 与 1 的分界线,另一条是 1 与 2 的分界线(且两条分界线最多只能重合)不能相互交叉。然后求总的两条分界线的书目就可以啦。
接下来看LGV定理,因为这个定理只能用于求多个起点到多个终点的n条路径的不可以相交的路径情况数,而这道题目中有两条对起终点,且两条路径是可重合的,所以这道题需要做一下变形(0与1的那一条分界线可以左上移动一格变到另一个矩形中,如果两条线原本重合,移动后必然不会再相交)。
学长博客里面讲啦如何变形。
这张图就是可能出现两条路线重合的情况
红色线左上移动,就可以避免这种情况。
最后需要求的就是右上方的两个点定义为 a1(0,m), a2(1,m+1)到左下方的两个点 b1(n,0), b2(n+1,1);
这样套用LGV的公式求解应用行列式为2*2的
[
(
a
1
,
b
1
)
(
a
1
,
b
2
)
(
a
2
,
b
1
)
(
a
2
,
b
2
)
]
\begin{bmatrix} (a1,b1) &(a1,b2)\\ (a2,b1)&(a2,b2)\\ \end{bmatrix}
[(a1,b1)(a2,b1)(a1,b2)(a2,b2)]
(a1,b1)代表从起点a1,到终点b1的路径条数。
这个就是从总步数中选择那些步数是向下走的就是答案,当然选择那些步数是向左走的也是对的。(C几几)
[
c
(
n
,
n
+
m
)
c
(
n
+
1
,
n
+
m
)
c
(
n
−
1
,
n
+
m
)
c
(
n
,
n
+
m
)
]
\begin{bmatrix} c(n,n+m) &c(n+1,n+m)\\ c(n-1,n+m)&c(n,n+m)\\ \end{bmatrix}
[c(n,n+m)c(n−1,n+m)c(n+1,n+m)c(n,n+m)]
求出这个二阶行列式就是答案
杨辉三角式:
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const ll maxn =1e3+10;
const ll mod = 1e9 + 7;
ll c[maxn*2][maxn];
void init()
{
c[0][0]=1;
for(int i=1;i<=maxn*2-5;i++)
{
c[i][0]=1;
for(int j=1;j<=maxn-5;j++)
{
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
}
}
int main()
{
init();
int n,m;
ios::sync_with_stdio(false);
while(cin>>n>>m)
{
ll ans=c[n+m][n]*c[n+m][n]%mod-c[n+m][n-1]*c[n+m][n+1]%mod;//行列式计算。
ans=(ans%mod+mod)%mod;
cout<<ans<<endl;
}
return 0;
}
逆元求阶乘式:
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const ll maxn =1e3+10;
const ll mod = 1e9 + 7;
// ll c[maxn*2][maxn];
ll f[maxn*2];
ll infac[maxn];
ll gett(ll n,ll b)
{
ll res=1%mod;
while(b)
{
if(b&1)res=res*n%mod;
n=n*n%mod;
b>>=1;
}
return res;
}
void init()
{
// c[0][0]=1;
f[0]=1;
infac[0]=1;
for(int i=1;i<=maxn*2-5;i++)
{
f[i]=i*f[i-1]%mod;
if(i<maxn)
infac[i]=gett(f[i],mod-2);
}
}
int main()
{
init();
int n,m;
ios::sync_with_stdio(false);
// for(int i=0;i<=10;i++)
// {
// cout<<f[i]<<" "<<infac[i]<<endl;
// }
while(cin>>n>>m)
{
// ll ans=c[n+m][n]*c[n+m][n]%mod-c[n+m][n-1]*c[n+m][n+1]%mod;//行列式计算。
ll ans1=f[n+m]*infac[n]%mod*infac[m]%mod*f[n+m]%mod*infac[n]%mod*infac[m]%mod;
ll ans2=f[n+m]*infac[n-1]%mod*infac[m+1]%mod*f[n+m]%mod*infac[n+1]%mod*infac[m-1]%mod;
ll ans=ans1-ans2;
ans=(ans%mod+mod)%mod;
cout<<ans<<endl;
}
return 0;
}
B:Symmetric Matrix Gym - 247727B
学长博客
这道题目的题意:
1,.给出一个n*n的矩阵
2.每一个方块里面只能放0,1,2
3.每一行的sum总值是2
4.对角线上的值总为零,并且除去对角线之后,每一个方块的值关于对角线对称(对角线为左上到右下的那一条)
这道题目
1,首先图中某一点非零那么该点的 (i,j) 代表第i行与第j列链接啦一条边(通过对角线2,对称另一个点为 (j,i) )
3,如果一行的某一个值为2,那么代表当前这一行与另一行已经构成啦一个环,且不能加入其他环。
4,如果是1,还可以加入其他的行组成更大的环,且不能再加入值为2的点。
5,因为每一行最后都是总sum都是为2,所以不存在有行不与其他行链接的情况。
所以这个题就成啦,给你这些行能构成环的多少中情况
上图中的点该是行。
假设现在选取前n-1行已经加入其中。那么
f
[
0
]
f[0]
f[0]到
f
[
n
−
1
]
f[n-1]
f[n−1]的值已经求出。
1,如果只选一行出来,那么就是(n-1)F[n-2]种情况。(此时不会除以2,下面的情况因为连接串的对称性会除以2)
2,选出多行时,假设选出n-k-1个点,留下k个点,(0<=k<=n-3)(一共n-1个点,选出超过两个)
例如:选出的n-k-1个点为五个,然后他们以次序54321或者12345链接到第n个点上,的结果是一样的。也就出现啦对称重复。
那么公式变成:c(n-1,k)f[k](n-k-1)!/2;
再加上枚举从0到n-3的所有情况相加。
f[n]=(n-1)f[n-2]+
1
/
2
1/2
1/2
∑
k
=
0
n
−
3
\sum_{k=0}^{n-3}
∑k=0n−3
C
n
−
1
k
C^k_{n-1}
Cn−1kf[k](n-k-1)!;
这道题目不支持n*n求解
所以需要简化公式:
O(n)求解
需要先把1,2,3的状态算出来。
分别为0,1,1;
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef long long ll;
typedef pair<int, int> PII;
const ll maxn =1e5+1000;
ll f[maxn];
int main()
{
ios::sync_with_stdio(false);
ll mod,n;
while(cin>>n>>mod)
{
f[1]=0;
f[2]=1;
f[3]=1;
for(ll i=4;i<=n;i++)
{
f[i]=(i-1)*f[i-1]%mod+(i-1)*f[i-2]%mod-(i-1)*(i-2)/2%mod*f[i-3]%mod;
f[i]=(f[i]%mod+mod)%mod;
}
cout<<f[n]%mod<<endl;
}
return 0;
}