HAOI2011]Problem c

XXI.[HAOI2011]Problem c

这题还是挺简单的~~~

关于每个位置i,在一种合法的方案 a 中,必有

(j=1n[aji])ni+1

因为,每一个aji都会占据i以后的某个位置,而i后面共有ni+1个位置,因此这是充分必要条件。

因此我们发现,这个入座的顺序对答案并无影响——因为上面的判别式并没有对下标的操作。因此,对于那贿赂上司的m个人,我们只需要关注那个qi即可。

我们设numi=(ni+1)j=1m[qji],即先减去已经确定的部分。

然后,我们从后往前确认每个位置填什么。

f[i][j]表示:

i个位置,

还有ja没有确定,

的方案数。

初始状态为f[n+1][nm]=1,其它都为0

则有f[i][jk]=f[i+1][j]Cjk

状态的含义:我们从i+1位置剩下的j个人中,挑选出k个人令他们的ax=i。因为顺序无关,所以是Cjk

至于这个kk[0,min(numi(nmj),j)],因为在位置i前面已经填入的(nmj)个人也会对i造成影响。

我们最后要判断f[1][0]是否被更新过,而不是判断它是否非0,因为可能会出现答案是模数倍数的情况。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,T,mod,num[310],f[310][310],C[310][310];
bool upd[310][310];
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d",&n,&m,&mod),memset(f,0,sizeof(f)),memset(upd,0,sizeof(upd)),memset(C,0,sizeof(C)),memset(num,0,sizeof(num));
		for(int i=1;i<=n;i++)num[i]=n-i+1;
		for(int i=0;i<=n;i++)C[i][0]=1;
		for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
		for(int i=1,x,y;i<=m;i++){
			scanf("%d%d",&x,&y);
			for(int j=1;j<=y;j++)num[j]--;
		}
		f[n+1][n-m]=upd[n+1][n-m]=1;
		for(int i=n;i>=1;i--)for(int j=0;j<=n-m;j++){
			if(num[i]-((n-m)-j)<0)continue;
			for(int k=0;k<=min(num[i]-((n-m)-j),j);k++)upd[i][j-k]|=upd[i+1][j],f[i][j-k]=(1ll*f[i+1][j]*C[j][k]+f[i][j-k])%mod;
		}
		if(!upd[1][0])puts("NO");
		else printf("YES %d\n",f[1][0]);
	}
	return 0;
}

posted @   Troverld  阅读(44)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示