1. 程式人生 > >Codeforces Round #458 (Div. 1 + Div. 2)C. Travelling Salesman and Special Numbers

Codeforces Round #458 (Div. 1 + Div. 2)C. Travelling Salesman and Special Numbers

C. Travelling Salesman and Special Numbers
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

The Travelling Salesman spends a lot of time travelling so he tends to get bored. To pass time, he likes to perform operations on numbers. One such operation is to take a positive integer x and reduce it to the number of bits set to 1 in the binary representation of x. For example for number 13 it’s true that 1310 = 11012, so it has 3 bits set and 13 will be reduced to 3 in one operation.

He calls a number special if the minimum number of operations to reduce it to 1 is k.

He wants to find out how many special numbers exist which are not greater than n. Please help the Travelling Salesman, as he is about to reach his destination!

Since the answer can be large, output it modulo 109 + 7.
Input

The first line contains integer n (1 ≤ n < 21000).

The second line contains integer k (0 ≤ k ≤ 1000).

Note that n is given in its binary representation without any leading zeros.
Output

Output a single integer — the number of special numbers not greater than n, modulo 109 + 7.
Examples
Input

110
2

Output

3

Input

111111011
2

Output

169

Note

In the first sample, the three special numbers are 3, 5 and 6. They get reduced to 2 in one operation (since there are two set bits in each of 3, 5 and 6) and then to 1 in one more operation (since there is only one set bit in 2).

題意:給定一種reduce操作(將某數置成其二進位制表示下1的個數),某數reduce到1所需要的操作次數為其步長,給一個二進位制數,要求求出小於該數且步長為k的所有數的個數模1e9+7。

解析:2的1000次方是一個很大的數,但是隻要經過一次操作之後,就會變成一個小於等於1000的數,所以我們可以先預處理出來所有小於等於1000的數需要多少次操作轉化為1

現在的n需要經過x次操作變成1,那麼n經過一次操作得到的數就需要經過x-1次操作變成1

根據預處理的結果,我們可以找到哪些數經過x-1次操作變成1

如果找到了數r,那麼就需要在小於等於n的範圍內,排列r個1

要保證小於等於n的話,可以從最高位開始進行這樣的操作,now代表現在的位數,x表示現在需要排列的1的數量

當該位為1的時候,我們考慮:

如果不排該位,則在後面的len-now-1個位置中找x個位置 即C(len-now-1,x)

如果排該位,則x=x-1,now=now+1,繼續進行下一位操作

如果該位為0,

則只有直接x=x-1,now=now+1,進行下一位操作。

這樣就可以判斷x>=2的情況,而當x=0時,只有1滿足條件,所以答案為1,當x=1時,我們直接在預處理中找那些能1步操作得到1

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(int i=a;i<b;i++)
#define rep1(i,a,b) for(int i=a;i>=b;i--)
#define rson rt<<1|1,m+1,r
#define lson rt<<1,l,m
using namespace std;
const int MOD=1e9+7;
const int MAXN1=1005;
const int MAXN2=1005;
ll a[1010];//記錄i處理為1所需的次數
ll C[MAXN1+5][MAXN2+5],len;//C[i][j]i表示總位數,j表示1的個數
char str[1010];
ll cal(ll x)
{
   ll num=0;
   while(x>0)
   {
      if(x%2==1)
      num++;
      x/=2;
   }
   return num;
}
void init1()//預處理1-1000得到1需要的運算元
{
  for(ll i=1;i<=1005;i++)
  {
      int h=i;
      while(h!=1)
      {
          h=cal(h);
          a[i]++;
      }
  }
}
void init2()  //預處理組合數
{
    for(int i=0;i<=MAXN1;i++)
        C[i][0]=1;
   for(int i=1;i<=MAXN1;i++)
   {
       for(int j=1;j<=i;j++)
        C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;//加這一位為0和加這一位為1
   }
}
ll so(ll x,ll now)//1的個數,現在長度
{
    if(now>=len)
        return 0;
    ll sum=0;
    if(str[now]=='1')
    {
        sum=C[len-now-1][x];//剩下的長度裡面取x個1
        if(x==1) return (sum+1)%MOD;
        return (sum+so(x-1,now+1))%MOD;
    }
    else
        return (sum+so(x,now+1))%MOD;
}
int main()
{
    init1();
    init2();
    ll ans,x,flag,rr;
    cin>>str>>x;
    len=strlen(str);
    if(x==0)
    {
      puts("1");
      return 0;
    }
    if(len==1&&str[0]=='1')
    {
        puts("0");
        return 0;
    }
    ans=0;
    for(int i=1;i<=len;i++)//列舉總長度
    {
      if(a[i]==x-1)
      {
        ans+=so(i,0)%MOD;
        ans%=MOD;
      }
    }
    if(x==1)
        cout<<ans-1<<endl;
    else
        cout<<ans<<endl;
    return 0;
}