Live2D

[20190927机房测试] matrix

求出满足以下条件的 n*m 的 01 矩阵个数:
(1)第 i 行第 1~li 列恰好有 1 个 1。
(2)第 i 行第 ri~m 列恰好有 1 个 1。
(3)每列至多有 1 个 1

这题还是很有趣的,模拟一下样例就想出dp方法了
最难想到的是要按列来枚举……
详细见代码注释吧

代码:

#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
#define N 3005
#define pos1 (rsum[i]-j+1)
#define pos2 (i-j-k)
using namespace std;

int n,m;
ll f[N][N],delta1,delta2;//f[i][j]表示前i列中有j列的右区间放了1 
ll lsum[N],rsum[N];//1~l和r~m已经放了多少个1 

struct Limit
{
	ll l,r;
}a[N];

template<class T>inline void read(T &res)
{
	char c;T flag=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
	while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}

ll Mod(ll x){return x%mod;}

int main()
{
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	read(n);read(m);
	for(register int i=1;i<=n;++i)
	{
		read(a[i].l);
		read(a[i].r);
		lsum[a[i].l]++;
		rsum[a[i].r]++;
	}
	f[0][0]=1;
	for(register int i=1;i<=m;++i)//枚举前 i 列 
	{
		f[i][0]=f[i-1][0];
		lsum[i]=lsum[i]+lsum[i-1];
		rsum[i]=rsum[i]+rsum[i-1];
		for(register int j=1;j<=i;++j)
		{
			delta1=Mod(f[i-1][j]);//右边不放1,直接转移 
			delta2=Mod(f[i-1][j-1]*pos1);//右边放1,方案数乘上剩余的位置 
			f[i][j]=Mod(delta1+delta2);
		}
		for(register int j=lsum[i-1];j<=lsum[i]-1;++j)//枚举左边还差多少个1 
		{
			for(register int k=0;k<=i;++k)//共有k个右区间放了1 
				f[i][k]=Mod(f[i][k]*pos2);//贡献=方案数*剩余位置 
		}
	}
	printf("%lld\n",f[m][n]);
	return 0;
}
/*
5 200
60 170
50 120
80 90
70 110
80 100
*/
posted @ 2019-09-27 19:46  tqr06  阅读(231)  评论(1编辑  收藏  举报