USACO 2012 February Silver

Description:
求第 n n n个刚好有 k k k 1 1 1的二进制正整数。
n ≤ 1 0 30 , 2 ≤ k ≤ 20 n\le10^{30},2\le k\le20 n1030,2k20,答案长度不超过 1000 1000 1000

Solution:

  • 比较常规的组合数,对于每一位 i i i,我们判断 C n − 1 i − 1 C_{n-1}^{i-1} Cn1i1是否大于当前的值即可。
  • 再套一个高精度。

Code:

#include
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i=i##_end_;--i)
#define ll long long
templateinline bool chkmin(T&x,T y){return x>y?x=y,1:0;}
templateinline bool chkmax(T&x,T y){return xinline void rd(T&x){
	x=0;char c;
	while((c=getchar())<48);
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
}

const int N=302;

int K;

#define L 255
#define P 10000
 
struct Big{
    int num[L],len;
    Big(){
        memset(num,0,sizeof(num));
        len=1;
    }
    void Rd(){
        char A[N];
        scanf("%s",A);
        int SL=strlen(A);
        len=0;
        for(int i=SL-1,res;res=0,i>=0;num[len++]=res,i-=4){
            if(i>=3)for(int j=i-3;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
            else for(int j=0;j<=i;j++)res=(res<<1)+(res<<3)+(A[j]^48);
        }
    }
    void Pr(){
      	if(!num[len-1]){putchar("0");return;}
        printf("%d",num[len-1]);
        DREP(i,len-2,0)printf("%04d",num[i]);       
    }
    Big operator +(const Big &a)const{
        Big b;
        b.len=max(len,a.len);
        REP(i,0,b.len-1){
            int &B=b.num[i];
            B+=num[i]+a.num[i];
            if(B>=P)B-=P,b.num[i+1]++;
        } 
        if(b.num[b.len])b.len++;
        return b;
    }
    Big operator+(int a){
        Big b;
        b.len=len;
        REP(i,0,len-1){
            int &B=b.num[i];
            B+=num[i]+a%P;
            if(B>=P)b.num[i+1]++,B-=P;
            a/=P;
        }
        if(b.num[b.len])b.len++;
        return b;
    }
    bool operator<=(const Big &a)const{
        if(len!=a.len)return lenpos[i+1])pos[i]--,putchar('0');
    }
    putchar('\n');
	
	return 0;
}

你可能感兴趣的:(USACO)