牛客网暑期ACM多校训练营(第一场)AB

本文探讨了两道编程题目:一是递增矩阵中的路径数量计算,利用LGV定理解决;二是对称矩阵中特定环状结构的数量统计。通过分析问题特点并采用数学方法简化计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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(n1,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[n1]的值已经求出。
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=0n3 C n − 1 k C^k_{n-1} Cn1kf[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;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值