牛客假日团队赛5 F- 随机数(组合数学)

随机数

https://ac.nowcoder.com/acm/contest/984/F

可以使用组合数学或者数位dp来写
组合数的写法, 就是通过枚举1 和 0的个数来计算总的方案数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll pre[110];
ll C(ll n, ll m) // 算组合数
{
    ll res1 = 1, j = 1;
    if (n / 2 < m)
        m = n - m;
    for (ll i = n - m + 1; i <= n; i++)
    {
        res1 *= i;
        while (res1 % j == 0 && j <= m)
        {
            res1 /= j;
            j++;
        }
    }
    return res1;
}
ll fun(ll a)
{
    ll u = 0, v = 0;
    ll ans = 0;
    ll x = a;
    ll tot = 0;
    while (x)
    {
        if (x & 1)
            u++;
        else
            v++;
        tot++;     // 该数字二进制位数
        x >>= 1;
    }
    if (u <= v && u) // 未排列也满足条件
        ans++;
    ll cnt1 = 1;
    ll cnt2 = 0;
    for (ll i = tot - 2; i >= 0; i--) // 枚举原数
    {
        if ((1LL << i) & a)   // 对二进制位上的1操作
        {
            cnt2++; // 把当前的1变为0
            if (i == 0)
            {
                if (cnt1 <= cnt2)
                    ans++;
                continue;
            }
            for (ll j = 0; cnt1 + j <= (i - j) + cnt2; j++) // 枚举1的个数
            {
                if (i >= j)   // 后面可以排的位置要大于枚举的1的个数
                    ans += C(i, j);
            }
            cnt2--;  // 再变回来
            cnt1++;  // 加上1的个数
        }
        else
            cnt2++;  // 0++
    }
    if (tot <= 1)
        return 1;
    for (ll i = 1; i < tot; i++)  // 加上低层的情况(因为低层小于当前位数, 任意排列均不会大于原数)
        ans += pre[i];
    return ans;
}
int main(){
    for (ll i = 1; i <= 32; i++)
    {
        for (ll j = 0; 1 + j <= i - 1 - j; j++) // j 是枚举 1的个数
        {
            pre[i] += C(i - 1, j);   // 打表,算每层的排列数目
        }
    }
    pre[1] = 1;
    ll a, b;
    while (cin >> a >> b)
        cout << fun(b) - fun(a - 1) << endl;
    return 0;
}
全部评论

相关推荐

牛客722552937号:新锐之星有点坑爹,特别是对男的
点赞 评论 收藏
分享
找不到工作死了算了:没事的,雨英,hr肯主动告知结果已经超越大部分hr了
点赞 评论 收藏
分享
今天 11:23
重庆邮电大学 C++
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务