带延迟的动态规划--51nod1327棋盘游戏

传送门
看到数据范围就觉得这一定是一个 n 2 ∗ m n^2*m n2m或者 n ∗ m 2 n*m^2 nm2 d p   q w q dp\ qwq dp qwq
果然是这样,因为每一列都是填一个或者不填,所以一定是按列 d p dp dp

但这个 d p dp dp和以往有些不同,一般 d p dp dp状态设计都只跟当前状态有关,并不关心前面的和后面的,但因为这道题中 l i 和 r i l_i和r_i liri有一些不同,当当前枚举的列数 i i i不断增大时,有一些之前可以填的 l l l到后面就不能填了,但 r r r只要当前能填,后面就一直能填 . . .

所以就有了带延迟的动态规划:
也就是说我们可以把当前可以处理但却没有处理的状态记下来,到后面因为某些限制而必须处理的时候再处理

f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示当前处理到 i i i列,前面空下了 j j j列,当前可以处理的 r r r k k k个没有处理的方案数,当我们还没有碰到某个 l l l的边界,就不会填,只有碰到 l l l的边界的时候,再从前面空下的 j j j列中找一列填上,这个可以用排列计算;而对于 r r r,只要碰到了边界,我们就可以选择处理或者不处理;当然这一列还可以选择不填或者填在 l , r l,r l,r中间的那一段,也就是既不在 l l l里,也不再 r r r里的 . . .

而且对这个状态从 i − 1 i-1 i1 i i i转移很不好写,还需要很多东西,我们可以从 i i i转移到它能够转移的状态去

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 55
#define M 205
#define LL long long
using namespace std;
int n,m,l[N],r[N],al[M],ar[M],cl,cr,tot;
LL f[M][M][N],fac[M],inv[M],ans;
const int mod=1e9+7;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

inline LL qpow(LL x,int k){
	LL ret=1;
	while(k){
		if(k&1) (ret*=x)%=mod;
		(x*=x)%=mod; k>>=1;
	} return ret;
}

inline void pre(){
	fac[1]=1;
	for(int i=2;i<=m+1;i++) fac[i]=fac[i-1]*i%mod;
	inv[m+1]=qpow(fac[m+1],mod-2);
	for(int i=m+1;i;i--) inv[i-1]=inv[i]*i%mod;
}

inline LL P(int n,int m){
	if(m==0) return 1;
	if(n<m) return 0;
//	if(n==m) return 1;多加了这个特判wa了一次我怕不是个zz 
	return fac[n]*inv[n-m]%mod;
}

inline void add(LL &x,LL y){
	x+=y; x>=mod?x-=mod:0;
}

int main(){
	n=rd(); m=rd();
	for(int i=1;i<=n;i++) l[i]=rd(),r[i]=rd(),al[l[i]]++,ar[m-r[i]+1]++;
	pre(); f[1][0][0]=1;
	for(int i=1;i<=m;i++){
		tot+=al[i-1]-ar[i];
		for(int j=max(0,al[i]-1);j<=i-1-cl;j++)
			for(int k=0;k<=cr;k++){
				if(!f[i][j][k]) continue;
				if(al[i])//i这一列填在l上 
				add(f[i+1][j-al[i]+1][k+ar[i]],f[i][j][k]*P(j,al[i]-1)%mod*al[i]%mod);
				if(k+ar[i] && j>=al[i])//i这一列填在r上,注意这里是k+ar[i] 
				add(f[i+1][j-al[i]][k+ar[i]-1],f[i][j][k]*P(j,al[i])%mod*(k+ar[i])%mod);
				if(tot && j>=al[i])//i这一列填在既不是l也不是r的位置上 
				add(f[i+1][j-al[i]][k+ar[i]],f[i][j][k]*P(j,al[i])%mod*tot%mod);
				if(j>=al[i])//i这一列空下 
				add(f[i+1][j-al[i]+1][k+ar[i]],f[i][j][k]*P(j,al[i])%mod);
			}
		cl+=al[i]; cr+=ar[i];
	}
	for(int i=0;i<=m;i++) (ans+=f[m+1][i][0])%=mod;	
	printf("%lld\n",ans);
	return 0;
}
/*
3 7
1 2
4 3
2 1
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值