[HAOI2011]Problem c

链接 P2523 [HAOI2011]Problem c

  • 想法还是很巧妙的。
  • 其实只是问一个先后顺序,因为编号相同的话,那么\(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\n",f[1][n-m]);
}
int main(){
    cin>>t;
    while(t--)sol();
    return 0;
}

posted @ 2018-10-22 22:19  Tyher  阅读(93)  评论(0编辑  收藏  举报