题目
分析
公式
先分析约束条件:
- r至少2位;
- r每一位的数都严格比右边数位的数小,即r各个位上的数字是递增的;
- 将该数转化为2进制后,总位数不能超过w。(举几个例子发现,一个n位的进制数,转成二进制之后的位数为nk,所以r的位数不能超过。
因为不一定是0,所以首位转二进制后可能并不是的完整的一段,需要特殊考虑。
首先看首位填0的情况,剩下的每一位都可以选择种数中的一个,由于数字各个位是递增的,只有一种排列方式,所以这种情况的数有个
再来考虑最高位不为0的情况,由于位数最多w,所以最高位转二进制后的位数最多为,故除了0以外,最多可以填的数有种,由于各个位上的数字是递增的,所以只能从大于最高位的数里面选,故这种情况下的数有个
结论:设满足题意的数有个,则可以如下计算:
高精组合数
由于答案很大,longlong存不下,需要高精。由于要算组合数,最大的问题就是设计计算的函数
先回顾公式
需要高精除,但是高精除代码长,复杂度也不低,所以需要更理想的方案。
由于组合数一定是整数,所以分子和分母一定可以约干净。
所以,我们可以约分掉分母,在把约分后的分子相乘,就能得到,然后相加至total
计算代码
const int N = 400;
int total[N];
int gcd(int a, int b){
if(b == 0) return a;
return gcd(b, a % b);
}
void cc(int n, int m){
if(n < m) return;
int a[N], b[N], i, j, x;
for(i = m; i >= 1; i--){
a[i] = n + i - m;
b[i] = i;
}
for(i = 1; i <= m; i++){
if(b[i] == 1) continue;
for(j = m; j >= 1; j--){
x=gcd(b[i], a[j]);
b[i] /= x;
a[j] /= x;
if(b[i] == 1) break;
}
}
memset(b, 0, sizeof b);
b[1] = b[0] = 1;
int g = 0;
for(j = 1; j <= m; j++){
g = 0;
if(a[j] == 1) continue;
for(i = 1;i <= b[0]; i++){
b[i] = b[i] * a[j] + g;
g = b[i] / 10;
b[i] %= 10;
if(i == b[0] && g != 0) b[0]++;
}
}
total[0] = max(total[0], b[0]);
for(int i = 1; i <= total[0]; i++){
total[i] += b[i];
total[i + 1] += total[i] / 10;
total[i] %= 10;
}
if(total[total[0] + 1] != 0) total[0]++;
}
代码
#include<iostream>
#include<cstring>
using namespace std;
const int N = 400;
int total[N];
int gcd(int a, int b){
if(b == 0) return a;
return gcd(b, a % b);
}
void cc(int n, int m){
if(n < m) return;
int a[N], b[N], i, j, x;
for(i = m; i >= 1; i--){
a[i] = n + i - m;
b[i] = i;
}
for(i = 1; i <= m; i++){
if(b[i] == 1) continue;
for(j = m; j >= 1; j--){
x=gcd(b[i], a[j]);
b[i] /= x;
a[j] /= x;
if(b[i] == 1) break;
}
}
memset(b, 0, sizeof b);
b[1] = b[0] = 1;
int g = 0;
for(j = 1; j <= m; j++){
g = 0;
if(a[j] == 1) continue;
for(i = 1;i <= b[0]; i++){
b[i] = b[i] * a[j] + g;
g = b[i] / 10;
b[i] %= 10;
if(i == b[0] && g != 0) b[0]++;
}
}
total[0] = max(total[0], b[0]);
for(int i = 1; i <= total[0]; i++){
total[i] += b[i];
total[i + 1] += total[i] / 10;
total[i] %= 10;
}
if(total[total[0] + 1] != 0) total[0]++;
}
int main(){
int k, w, n, m, c, i;
memset(total,0,sizeof total);
cin >> k >> w;
n = (1 << k) - 1; m = w / k; c = w % k;
for(i = m; i >= 2; i--) cc(n, i);
c = (1 << c) - 1;
if(c >= 1 && n > m)
for(i = 1; i <= c; i++) cc(n - i, m);
for(i = total[0]; i >= 1; i--) cout << total[i];
return 0;
}