Codeforces Round #458 C Travelling Salesman and Special Numbers
阿新 • • 發佈:2019-01-31
題意:給定一種reduce操作(將某數置成其二進位制表示下1的個數),某數reduce到1所需要的操作次數為其步長,給一個二進位制數,要求求出小於該數且步長為k的所有數的個數模1e9+7。
思路:
注意到所有範圍內的數的二進位制表示下1的個數不會超過1000,所以所有數經一步操作後會落在1-1000範圍內,同理,再操作一次會
落到1-10的範圍,而1-10內步長最長的為7(步長為3),所以所有範圍內的數步長不會超過5。此外,步長為k的數的二進位制表示中1
的個數必然為一個步長為k-1的數。所以,要求1-2^1000中數的步長可以由1-1000中數的步長+1得到。
不妨先寫一個函式,它的功能為求出小於給定n的二進位制表示中1的個數為num的數的數量模1e9+7。
故對於給定的n和k,我們先求出1-1000內所有數的步長,對於給定的k,遍歷1-n.len,找到步長為k-1的數作為上述函式的輸入,累加
即可。
AC程式碼如下:
#include <iostream> #include <algorithm> #include <cstring> using namespace std; #define MOD 1000000007 string n; int len; int k; int record[1006][1005]; int tttt[10] = {1,2,4,8,16,32,64,128,256,512}; void init() { record[1005][1] = 0; record[1005][2] = 1; record[1005][3] = 2; record[1005][4] = 1; record[1005][5] = 2; record[1005][6] = 2; record[1005][7] = 3; record[1005][8] = 1; record[1005][9] = 2; record[1005][10] = 2; for(int i=11;i<=1000;++i) { int cnt = 0; int n = i; int j = 9; while(n<tttt[j]) j--; while(n) { while(n>=tttt[j]) n -= tttt[j],cnt++; j--; } record[1005][i] = record[1005][cnt]+1; } for(int i=1;i<=1000;++i) record[i][0]=record[i][i]=1; for(int i=1;i<=1000;++i) { for(int j=1;j<i;++j) record[i][j] = (record[i-1][j]+record[i-1][j-1])%MOD; } } int ca(int num) { if(num>len) return 0; else if(num==1) return len-1; int ans = 0; int cur = 1; for(int i=1;i<len;++i) { if(n[i]=='1') { int temp =record[len-i-1][num-cur]; ans = (ans + record[len-i-1][num-cur])%MOD; cur++; if(cur==num) {ans=(ans+1)%MOD; break;} } } for(int i=1;i<=len-num;++i) { ans = (ans+record[len-i-1][num-1])%MOD; } return ans; } int solve() { if(k==0) return 1; if(k>5) return 0; int ans = 0; for(int i=1;i<=len;++i) { if(record[1005][i]==k-1) ans = (ans + ca(i))%MOD; } return ans; } int main() { ios::sync_with_stdio(0); cin.tie(0); init(); // for(int i=1;i<=1000;++i) cout<<i<<" "<<record[0][i]<<endl; while(cin>>n>>k) { len = n.length(); cout<<solve()<<endl; } return 0; }