【NOIP2012模拟8.7】奶牛编号

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Solution

对于这道题,我们先设0放x个,1放k个k个
设当前剩下x'个0和k'个1,则对于剩下的位置,我们可以把它抽象成将x'个0插入到x'+k'个位置中,方案数为\(C_{x'+k'-1}^{x'}\)
因此我们可以先枚举放置的0的个数,当总方案数\(\geqslant\)n时,那么我们要求的答案长度便求了出来
于是我们可以暴力枚举了
即使我们知道了答案的长度,暴力枚举仍然不在考虑范围内,因为它还是会炸
而上文提到的组合数又派上了用场
我们用组合数计算出当前这个位置放0的方案数,若其小于当前要求的n,则说明我们要求的答案在该位一定放1,否则一定放0。这样我们就可以一路递推下去,以O(len)的时间算出答案。

友情提示:作者组合数使用了二维,理论极限数据过不去,但数据过水再加点小优化就卡过去了,正解好像要用一维的来着。

Code

#include <cstdio>
using namespace std;
int n,k,len,x,i,j,f[3000001],c[3001][3001];
long long p,num;
int C(int x,int y)
{
    if (y==0 || x==y) return 1;
    if (y==1 || y==x-1) return x;
    return c[x][y]; 
}
void dg(int x,int y,int z)
{
    int g;
    if (x>len)
    {
       for (int i=1;i<=len;i++)
            printf("%d",f[i]);
        return;
    }
    if (x==1) 
    {
        f[1]=1;
        dg(x+1,y,z);
    }else
    {
        if (z==len-x+1)
        {
            dg(len+1,y,z);
            return;
        }
        g=C(len-x,z-1);
        if (g>=y) 
        {
            f[x]=0;
            dg(x+1,y,z-1);
        }else
        {
            f[x]=1;
            dg(x+1,y-g,z);
        }
    }
}
int main()
{
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    scanf("%d%d",&n,&k);
    if (k==1)
    {
        printf("1");
        for (i=1;i<n;i++)
            printf("0");
        return 0;
    }
    c[1][1]=c[1][0]=1;
    for (i=2;i<=3000;i++)
    {
        c[i][0]=1;
        for (j=1;j<=i;j++)
            c[i][j]=c[i-1][j-1]+c[i-1][j];
    }
    for (i=0;i<=n;i++)
        if (c[k+i-1][i]+p>=n)
        {
            len=k+i;
            break;
        }else p+=c[k+i-1][i];
    n-=p;
    dg(1,n,len-k);
}

posted @ 2020-08-12 07:52  Sport_River  阅读(100)  评论(0编辑  收藏  举报