UOJ Logo zhouzixuan的博客

博客

bzoj 2302: [HAOI2011]Problem c

2016-03-13 16:57:16 By zhouzixuan
【题目描述】:
    给n个人安排座位,先给每个人一个1~n的编号,设第i个人的编号为ai(不同人的编号可以相同),接着从第一个人开始,大家依次入座,第i个人来了以后尝试坐到ai,如果ai被占据了,就尝试ai+1,ai+1也被占据了的话就尝试ai+2,……,如果一直尝试到第n个都不行,该安排方案就不合法。然而有m个人的编号已经确定(他们或许贿赂了你的上司...),你只能安排剩下的人的编号,求有多少种合法的安排方案。由于答案可能很大,只需输出其除以M后的余数即可。
【题目解法】:
    考虑最后一定是每一个人都入座
    因此合法的充要条件就是:编号<=i的至少有i个人
    这样我们就可以无视入座的顺序了,那么我们考虑动态规划
    令f[i][j]表示编号<=i的有j个人
    每次枚举编号为i+1的有几个人
    令s[i]表示已经确定的编号>=i的人数,a[i]表示确定的编号=i的人数
    f[i][j]*C[n-j-s[i+1]][k-a[i+1]]->f[i+1][j+k] (j>=i)
    答案就是f[n][n]啦
    然后就可以WA了TAT
    注意题目说无解输出NO,这并不是出题人SB的做法,而是出题人BS你的做法
    因为f[n][n]=0并不说明无解,可能说明它的方案数mod M=0,因此我们需要令开一个bool记录g[i][j]是否有解
    复杂度O(N^3)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=305;
int n,m,c[N][N],a[N],s[N],f[N][N];
bool g[N][N]; int cases,maxmod;
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
void init()
{
    n=getint(); m=getint(); maxmod=getint();
    memset(a,0,sizeof(a)); memset(s,0,sizeof(s));
    memset(c,0,sizeof(c)); memset(g,false,sizeof(g));
    for (int i=0; i<=n; i++)
    {
        c[i][0]=1;
        for (int j=1; j<=i; j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%maxmod;
    }
    for (int i=1; i<=m; i++) {int x=getint(),y=getint(); a[y]++;}
    for (int i=n; i>=1; i--) s[i]=s[i+1]+a[i];
}
void solve()
{
    memset(f,0,sizeof(f)); f[0][0]=1; g[0][0]=true;
    for (int i=0; i<n; i++)
        for (int j=i; j<=n; j++)
        {
            if (f[i][j]==0) continue;
            for (int k=a[i+1]; k<=n-j-s[i+1]+a[i+1]; k++)
            {
                //f[i][j]*C[n-j-s[i+1]][k-a[i+1]]->f[i+1][j+k]
                long long val=(long long)f[i][j]*c[n-j-s[i+1]][k-a[i+1]]%maxmod;
                f[i+1][j+k]=(f[i+1][j+k]+val)%maxmod; g[i+1][j+k]=true;
            }
        }
    if (!g[n][n]) printf("NO\n"); else printf("YES %d\n",f[n][n]);
}
int main()
{
    cases=getint();
    while (cases--)
    {
        init();
        solve();
    }
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。