HAOI2011]Problem c

XXI.[HAOI2011]Problem c

这题还是挺简单的~~~

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

\((\sum\limits_{j=1}^n[a_j\geq i])\leq n-i+1\)

因为,每一个\(a_j\geq i\)都会占据\(i\)以后的某个位置,而\(i\)后面共有\(n-i+1\)个位置,因此这是充分必要条件。

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

我们设\(num_i=(n-i+1)-\sum\limits_{j=1}^m[q_j\geq i]\),即先减去已经确定的部分。

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

\(f[i][j]\)表示:

\(i\)个位置,

还有\(j\)\(a\)没有确定,

的方案数。

初始状态为\(f[n+1][n-m]=1\),其它都为\(0\)

则有\(f[i][j-k]=\sum f[i+1][j]*C_{j}^{k}\)

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

至于这个\(k\)\(k\in\Big[0,\min\big(num_i-(n-m-j),j\big)\Big]\),因为在位置\(i\)前面已经填入的\((n-m-j)\)个人也会对\(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;
}

原文地址:https://www.cnblogs.com/Troverld/p/14596956.html