- 想法还是很巧妙的。
- 其实只是问一个先后顺序,因为编号相同的话,那么(id)小的就在前面,(id)大的就在后面。
- 所以我们考虑的是到底有哪一些人拿到的是相同的编号。
- 先考虑无解的情况,也就是如果编号(≥i)的人放不下了。
- 其他的情况都是有解的。
- 其实我们不需要关心那一些有人的地方,也就是我们现在可以把已经有人的地方扣出来,把确定的了位置的人也扣除来。
- 现在问题变成了有(n-m)个人,没有人确定位置的问题了。
- 考虑(f_{i,j})表示考虑了编号为(i)到(n)给谁,已经确定了(j)个人拿到的编号。
- 那么有$$f_{i,j}=∑f_{i+1,j-k}×C_{j}^{k} (0≤j≤p_i)$$
- 其中(p_i)表示编号大于(i)可以确定的人数。
- 这里的含义就是考虑在(j)个人中取出了(k)个人作为编号(i),因为人有编号,所以组合数一下。
- 答案(f_{n-m,0})
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define R register int
using namespace std;
const int N=400;
int n,m,t,u,v,mod,Q[N],s[N],C[N][N],f[N][N];
void add(R &x,R y){x=(x+y>=mod?x+y-mod:x+y);}
void sol(){
scanf("%d %d %d",&n,&m,&mod);
memset(f,0,sizeof(f));
memset(C,0,sizeof(C));
memset(s,0,sizeof(s));
for(R i=0;i<=n;++i)C[i][0]=1,s[i]=0;
for(R i=1;i<=n;++i)
for(R j=1;j<=i;++j)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
for(R i=n;i>=1;--i)s[i]=s[i+1]+1;
for(R i=1;i<=m;++i){
scanf("%d %d",&u,&v);
for(R j=1;j<=v;++j){
s[j]--;
if(s[j]<0){puts("NO");return;}
}
}
f[n+1][0]=1;
for(R i=n;i>=1;--i)
for(R j=0;j<=s[i];++j)
for(R k=0;k<=j;++k)
f[i][j]=(f[i][j]+1ll*f[i+1][j-k]*C[j][k]%mod)%mod;
printf("YES %d
",f[1][n-m]);
}
int main(){
cin>>t;
while(t--)sol();
return 0;
}