题意:

当矩阵中的(x,y)为所在行和所在列中最大的,该(x,y)被称为纳什均衡点。

问n*m的全排列矩阵最多只有一个纳什均衡点的方案有几种,对p取模。

其中

思路:

因为矩阵中最多只能有一个纳什均衡点,所以肯定是最大值的点。所以当放了最大值之后,第二大的值只能放在和它同行或同列的地方,同理,对于一个放完的点,下一个点只能放在和之前的点同行或同列的地方。

比赛的时候队友oeis搞过去了…搞出来的公式也是有点道理的,,因为如果有行都放满或者列都放满(并不仅仅是单纯一整行或一整列,对角线的情况也是)的的情况后,剩下的数就可以都随便放了,所以算有行或列放满的情况就是(就相当于对行标号,对列标号,标号相同的填上从小到大的数字),剩下的数就进行排列(后来想想好像也不太对,需要update)。

用上面的结论也是可以dp的。

:放完第k个点之后有i行j列被占的方案数。

有三种转移:

k放的位置列被放过,行没有被放过:

在放过的列里面选一列,在没放过的行里选一行。

k放的位置行被放过,列没有被放过:

k放的位置行和列都被放过:

放过的行和列中共有i*j个位置,放在没有放过的位置。

初始条件为

这就可以写了,但是这个数据有点大,这里需要剪掉一些,所以要把i和j放在外层,k的范围为2~i*j。

代码:

#define MOD 1000000007
ll dp[85][85][6500];//放置k后有i行j列被占了
int main()
{
    int t,n,m;
    ll p;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%lld",&n,&m,&p);
        dp[1][1][1]=1ll*m*n;
        for(int i=1;i<=n;i++)
            for(int j=(i==1?2:1);j<=m;j++)
                for(int k=2;k<=i*j;k++)
                {
                    ll temp1=0,temp2=0,temp3=0;
                    temp1=dp[i-1][j][k-1]*j*(n-i+1)%p;
                    temp2=dp[i][j-1][k-1]*i*(m-j+1)%p;
                    if(i*j-k+1>0)
                        temp3=dp[i][j][k-1]*(i*j-k+1)%p;
                    dp[i][j][k]=(temp1+temp2+temp3)%p;
                    //printf("%d %d %d:%lld\n",i,j,k,dp[i][j][k]);
                }
        printf("%lld\n",dp[n][m][n*m]);
    }
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

CTU2017D 哈希+乱搞 Previous
ZOJ4009 & 上海大都会赛H 循环节+线段树 Next