题解:qoj2568 Mountains

qoj2568

求有多少个 \(n \times m\) 的非负整数矩阵 \(a\),满足从左上角到右下角的任意一条路径长度都 \(\le k\)

\(n,m,k \le 100\)


我们考虑当矩阵 \(a\) 确定的时候,如何求出 \((1,1)) \to (n,m)\) 的最长路。

我们可以 dp。设 \(f_{i,j}\)\((i,j) \to (n,m)\) 的最长路。那么有转移

\[f_{i,j} = a_{i,j}+\max(f_{i+1,j},f_{i,j+1}) \]

牛逼点解析 1:矩阵 \(a\) 不好计数,我们转化为对 dp 矩阵 \(f\) 计数。

显然一个 \(a\) 可以确定唯一的 \(f\)。注意到 \(a_{i,j} = f_{i,j} - \max(f_{i+1,j},f_{i,j+1})\),因此一个 \(f\) 可以确定出唯一的 \(f\),即 \(a\)\(f\) 为一组双射。

因此问题转化为了求有多少个值域为 \([0,k]\) 的矩阵 \(f\) 且满足 \(f_{i,j} > \max(f_{i+1,j},f_{i,j+1})\)

这个条件的意思可以放大成每个位置的值一定比他下面和右边的格子大。

牛逼点解析 2:考虑画出 \(f\) 矩阵相邻两种值的分界线。

首先由条件,对于一个 \(x\),我们会发现 \(f\) 数组 \(> x\) 的数字和 \(\le x\) 的数字的分界线一定是呈现出上阶梯状。

然后我们会注意到,如果我们有了所有 \(k\) 条分界线,也能确定出唯一的 \(f\) 数组。因此我们由对矩阵的计数转化成了对分界线的计数。

问题转化成了,选出 \((1,1) \to (n,m)\)\(k\) 条两两不互相穿过的路径,由多少种方案?

牛逼点解析 3:将 \((1,1)\) 拆成 \(k\) 个起点,将 \((n,m)\) 拆成 \(k\) 个终点,转化为不交路径计数。

对于第 \(i\) 条路径,将起点往左平移 \(i\) 个单位,再往上平移 \(i\) 个单位;对于终点。

这样原来的穿过在新图上就体现为相交(重合),原来的允许重合在新图上就被拆开了。因此我们转化为了网格图上的不交路径计数,可以根据 LGV 引理解决。

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define N 206
#define MOD 1000000007
using namespace std;
int n,m,k,a[N][N],fac[N],ifac[N];
int qpow(int x,int y)
{
    if(y==0)return 1;if(y==1)return x%MOD;
    int ret=qpow(x,y>>1);return ret*ret%MOD*qpow(x,y&1)%MOD;
}
void init()
{
    fac[0]=1;
    for(int i=1;i<N;i++)fac[i]=fac[i-1]*i%MOD;
    ifac[N-1]=qpow(fac[N-1],MOD-2);
    for(int i=N-2;~i;i--)ifac[i]=ifac[i+1]*(i+1)%MOD;
}
int binom(int x,int y){return x<y?0:fac[x]*ifac[y]%MOD*ifac[x-y]%MOD;}
int get_det()
{
    int neg=0,ret=1,inv;
    for(int i=0;i<k;i++)
    {
        if(!a[i][i])
        {
            for(int j=i+1;j<k;j++)
                if(a[j][i]){swap(a[i],a[j]);break;}
            if(!a[i][i])return 0;
            neg^=1;
        }
        inv=qpow(MOD-a[i][i],MOD-2),ret*=a[i][i],ret%=MOD;
        for(int j=i+1,t;j<k;j++)
        {
            t=inv*a[j][i]%MOD,a[j][i]=0;
            for(int l=i+1;l<k;l++)a[j][l]+=t*a[i][l]%MOD,a[j][l]%=MOD;
        }
    }
    return neg?(MOD-ret)%MOD:ret;
}
main()
{
    scanf("%lld%lld%lld",&n,&m,&k),init();
    for(int i=0;i<k;i++)
        for(int j=max(0ll,i-m);j<k&&j<=n+i;j++)
            a[i][j]=binom(n+m,n+i-j);
    printf("%lld\n",get_det());
    return 0;
}
posted @ 2025-07-21 15:35  dyc2022  阅读(19)  评论(0)    收藏  举报
/* 设置动态特效 */ /* 设置文章评论功能 */ 返回顶端 levels of contents