[AGC039F] Min Product Sum


一、题目

点此看题

二、解法

考虑权值的组合意义,考虑一个矩阵 \(B\),它需要满足 \(b(i,j)\leq \min(a(i,k),a(k,j))\),进一步转化就是 \(B\) 第 \(i\) 行的最大值 \(\leq A\) 第 \(i\) 行的最小值,并且 \(B\) 第 \(j\) 列的最大值 \(\leq A\) 第 \(j\) 列的最小值,权值和就是合法的 \((A,B)\) 对数。

我们按照 \(k\) 从小到大地填到 \(A,B\) 里面,考虑到限制的特殊性,我们记录下 \(B\) 有多少行的最大值已经被确定了,\(A\) 有多少列的最小值已经被确定了,那么转移就考虑 \(B\) 中新增一些行的最大值 \(=k\) 和 \(A\) 中新增一些列的最小值 \(=k\):

  • 先新增 \(B\) 的行,考虑这个行上的每一个位置。如果对应的列上 \(A\) 的最小值已经确定(并且一定 \(),那么为了适配限制,我们往这个位置的 \(A\) 上填入一个 \(\geq k\) 的数;如果对应的列上 \(A\) 的最小值还未确定,那么可以在 \(B\) 中填入一个 \(\leq k\) 的数,并且要求这些数中至少有一个 \(=k\)
  • 再新增 \(A\) 的列,考虑这个列上的每一个位置。如果对应的行上 \(B\) 的最大值已经确定(并且一定 \(\leq k\)),那么为了适配限制,我们往这个位置的 \(A\) 上填入一个 \(\geq k\) 的数,并且要求这些数中至少有一个 \(=k\);如果对应的列上 \(B\) 的最大值还未确定,那么可以在 \(B\) 中填一个 \(\leq k\) 的数。

发现转移系数就是组合数(选出行列)乘上幂次(选数)的形式,所以可以 \(O(n^3)\) 预处理出转移系数,然后 \(O(n^4)\) 暴力 \(dp\)

#include 
#include 
using namespace std;
#define int long long
const int M = 105;
int read()
{
    int x=0,f=1;char c;
    while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
int n,m,k,p,C[M][M],g[M][M][M][2],f[M][M][M][2];
int sub(int a,int b) {return (a-b+p)%p;}
void add(int &x,int y) {x=(x+y)%p;}
int qkpow(int a,int b)
{
    int r=1;
    while(b>0)
    {
        if(b&1) r=r*a%p;
        a=a*a%p;
        b>>=1;
    }
    return r;
}
signed main()
{
    n=read();m=read();k=read();p=read();
    for(int i=0;i<=max(n,m);i++)
    {
    	C[i][0]=1;
    	for(int j=1;j<=i;j++)
    		C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
	}
    for(int c=1;c<=k;c++) for(int i=0;i<=m;i++)
    {
        int t=qkpow(k-c+1,i)*sub(qkpow(c,m-i)
            ,qkpow(c-1,m-i))%p;
        g[c][i][0][0]=1;
        for(int j=1;j<=n;j++)
            g[c][i][j][0]=g[c][i][j-1][0]*t%p;
    }
    for(int c=1;c<=k;c++) for(int i=0;i<=n;i++)
    {
        int t=qkpow(c,n-i)*sub(qkpow(k-c+1,i)
            ,qkpow(k-c,i))%p;
        g[c][i][0][1]=1;
        for(int j=1;j<=m;j++)
            g[c][i][j][1]=g[c][i][j-1][1]*t%p;
    }
    f[1][0][0][0]=1;
    for(int c=1;c<=k;c++) for(int i=0;i<=n;i++)
        for(int j=0;j<=m;j++)
        {
            for(int l=0;l<=n-i;l++)
                add(f[c][i+l][j][1],f[c][i][j][0]*
				C[n-i][l]%p*g[c][j][l][0]);
            for(int l=0;l<=m-j;l++)
                add(f[c+1][i][j+l][0],f[c][i][j][1]*
				C[m-j][l]%p*g[c][i][l][1]);
        }
    printf("%lld\n",f[k+1][n][m][0]);
}