[OICamp 2016 Day5A]鞍点

题目大意

一个n*m矩形,每个格子的数都在[1,p],求至少有一个鞍点的矩形个数。(i,j)为鞍点定义为(i,j)的权值在第i行和第j列都严格最大。

DP

我们设f[i,j]表示目前有i个鞍点,鞍点的权值不大于j的矩形个数。
我们把这i行i列摆在一起。
现在枚举权值为j+1的鞍点个数x,那么就是从f[i,j]推到f[i+x,j+1],选x行选x列的方案CxniCxmi,将它们任意排列还有x!的方案,然后鞍点所在行列其余部分都只能放<=j,方案为jx(ni)+x(mi)xxx

容斥原理

做完dp后,求答案可以容斥。
因为可以把有i个鞍点看做至少有i个。
所以ans=min(n,m)i=1(1)i1f[i,p]p(ni)(mi)
注意计算过程中所有求幂的我们都可以预处理来消掉log。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=2000+10;
int f[maxn][20],c[maxn][maxn],fac[maxn],mi[20][maxn*maxn];
int i,j,k,l,t,x,n,m,p,mo,ans;
int main(){
    scanf("%d%d%d%d",&n,&m,&p,&mo);
    if (n>m) swap(n,m);
    c[0][0]=1;
    fo(i,1,m){
        c[i][0]=1;
        fo(j,1,m) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
    }
    fo(i,1,p){
        mi[i][0]=1;
        fo(j,1,n*m) mi[i][j]=(ll)mi[i][j-1]*i%mo;
    }
    mi[0][0]=1;
    fac[0]=1;
    fo(i,1,m) fac[i]=(ll)fac[i-1]*i%mo;
    f[0][0]=1;
    fo(i,0,n)
        fo(j,0,p-1)
            fo(x,0,n-i)
                (f[i+x][j+1]+=(ll)f[i][j]*c[n-i][x]%mo*c[m-i][x]%mo*fac[x]%mo*mi[j][x*(n-i)+x*(m-i)-x*x-x]%mo)%=mo;
    fo(i,1,n){
        t=(ll)f[i][p]*mi[p][(n-i)*(m-i)]%mo;
        if (i%2==0) (ans-=t)%=mo;else (ans+=t)%=mo;
    }
    (ans+=mo)%=mo;
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值