jzoj 4779. 【GDOI2017模拟9.14】鞍点

2 篇文章 0 订阅

question

这题题目全是图,就不贴了(感觉好多题都是图。。。)

Solution

正解DP

我们设 f [ i ] [ j ] f[i][j] f[i][j]表示放到第i个鞍点,其中的最大值为j的方案数。
我们使放置的鞍点的大小严格不下降(这样计算较方便)
而后,我们将 f [ i ] [ j ] f[i][j] f[i][j]推到下一个: f [ i + 1 ] [ j + k ] ( 0 &lt; = k &lt; = n − j ) f[i+1][j+k](0&lt;=k&lt;=n-j) f[i+1][j+k]0<=k<=nj 这里的n<m
转移方程很容易得到:
f [ i + 1 ] [ j + k ] + = f [ i ] [ j ] ∗ y h [ k ] [ n − j ] ∗ y h [ k ] [ m − j ] ∗ j c [ k ] ∗ M [ i ] [ ( n − j ) ∗ k + ( m − j ) ∗ k − k ∗ k − k ] f[i+1][j+k]+=f[i][j] *yh[k][n-j] *yh[k][m-j] *jc[k]*M[i][(n-j) *k+(m-j) *k-k *k-k] f[i+1][j+k]+=f[i][j]yh[k][nj]yh[k][mj]jc[k]M[i][(nj)k+(mj)kkkk]
其中 y h [ i ] [ j ] yh[i][j] yh[i][j]表示 C ( i , j ) C(i,j) C(i,j),M[i][j]表示 i j i^j ij
最后我们就用容斥来搞一搞就可以了。

code

#include<cstdio>
#define N 2010
#define ll long long
using namespace std;
ll jc[N],yh[N][N],M[11][N*N],f[11][N],ans=0;
int n,m,K,mo;

void ycl()
{
	if (n>m) n=n+m,m=n-m,n=n-m;
	jc[0]=1;
	for (int i=1;i<=m;i++)
		jc[i]=jc[i-1]*i%mo,yh[1][i]=i;
	for (int i=2;i<=m;i++)
		for (int j=i;j<=m;j++)
			yh[i][j]=(yh[i][j-1]+yh[i-1][j-1])%mo;
	M[0][0]=1;
	for (int i=1;i<=K;i++)
	{
		M[i][0]=1;
		for (int j=1;j<=n*m;j++)
			M[i][j]=M[i][j-1]*i%mo;
	}
}

int main()
{
	scanf("%d%d%d%d",&n,&m,&K,&mo);
	ycl();f[0][0]=1;
	for (int i=0;i<K;i++)
		for (int j=0;j<=n;j++)
		{
			if (!f[i][j]) continue;
			(f[i+1][j]+=f[i][j])%=mo;
			for (int k=1;k<=n-j;k++)
				(f[i+1][j+k]+=f[i][j]*yh[k][n-j]%mo*yh[k][m-j]%mo*jc[k]%mo*M[i][(n-j)*k+(m-j)*k-k*k-k]%mo)%=mo;
		}
//	printf("%lld\n",f[K][n]);
	for (int i=1,j=1;i<=n;i++,j=-j)
		(ans+=f[K][i]*M[K][(n-i)*(m-i)]%mo*j%mo)%=mo;
	printf("%lld\n",(ans+mo)%mo);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值