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;
}