Atcoder Beginner Contest 356题解(D、E)

D. Masked Popcount

按位考虑 + 排列组合

考虑M=10110111001
i0循环到N

因为求的是所有i&M的二进制表示中1的个数,所以可以按位考虑,考虑有多少个ibit位与Mbit&出来是1

首先如果两个数bit&出来是1,那么这两个位一定都是1

假设当前bit7

bit: 10 9 8 7 6 5 4 3 2 1 0
M:    1 0 1 1 0 1 1 1 0 0 1
i:    _ _ _ 1 _ _ _ _ _ _ _

如果bit前面的位填1 0 1,则bit后面的位只能填00 1 1 1 0 0 1;方案数是1(M&0x11110000000+1)
如果bit前面的位填01 0 0,则bit后面的位可以填01 1 1 1 1 1 1。方案数是(i>>(bit+1))(1<<bit)

写代码时注意取模操作,传入函数中的参数也应该取模。

int n, m;

void solve() {
//    cout << ((1 << 9) + (1 << 7) + (1 << 6) + (1 << 4) + 1) << '\n';
    cin >> n >> m;
    int ans = 0ll;
    for (int r = 60ll; r >= 0ll; r --) {
        if (m >> r & 1ll) {
            if (n >> r & 1ll) {
                plut(ans, plu(mul((n >> r + 1ll) % mod, (1ll << r) % mod), (n & ((1ll << r) - 1ll)) % mod + 1ll));
            }
            else {
                plut(ans, mul((n >> r + 1ll) % mod, (1ll << r) % mod));
            }
        }
    }
    cout << ans % mod << '\n';
}

E. Max/Min

考虑如果a数组中的元素各不相同。

观察到a[i]1e6,可以开个桶来存储每个a[i]出现的次数,可以对这个桶数组再求个前缀和,则可以用这个前缀和数组O(1)地求出某一连续值域中出现的a[i]的个数。

现在如果我们(先将a排序以方便理解)从前往后枚举a中的每一个元素,求当前位置i后面有多少个j使得ajai等于某一个值x,我们发现这个x的最大值就是max(a[i])a[i]。对于每个x,我们可以用cnt[a[i]x+a[i]1]cnt[a[i]1]来得出a[i]对答案的贡献,假设m=max(a[i]),那么我们这样操作的时间复杂度就是i=1nma[i]<=i=1nmi,因为后者是调和级数,所以时间复杂度就是O(mlogn)

在代码实现中要注意特判a[j]a[i]1倍的情况并且注意把相同的a[i]内部的贡献加上。

int n;
int cnt[(int)2e6 + 10];
int s[(int)2e6 + 10];

void solve() {
    cin >> n;
    memset(cnt, 0, sizeof cnt);
    vector<int> a;
    int max_a = 0;
    for (int i = 0; i < n; i ++) {
        int x;
        cin >> x;
        a.push_back(x);
        cnt[x] ++;
        max_a = max(max_a, x);
    }
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
    int ans = 0;
    memset(s, 0, sizeof s);
    for (int i = 1; i <= (int)1e6; i ++) {
        s[i] = s[i - 1] + cnt[i];
    }
//    for (int i = 1; i <= 4; i ++) {
//        cout << s[i] << ' ';
//    }
//    cout << '\n';
//    for (int i = 0; i < a.size(); i ++) {
//        cout << a[i] << ' ';
//    }
//    cout << '\n';
//    for (int i = 0; i < a.size(); i ++) {
//        cout << cnt[a[i]] << ' ';
//    }
//    cout << '\n';
    for (int i = 0; i < a.size(); i ++) {
        int m = max_a / a[i];
        for (int j = 1; j <= m; j ++) {
//            cout << j << ' ' << j * a[i] + a[i] - 1 << ' ' << j * a[i] << '\n';
//            int ct = (s[j * a[i] + a[i] - 1] - s[(j == 1 ? j * a[i] : j * a[i] - 1)]) * cnt[a[i]] * j;
//            cout << ct << '\n';
            ans += (s[min(j * a[i] + a[i] - 1, (int)1e6)] - s[min((int)1e6, (j == 1 ? j * a[i] : j * a[i] - 1))]) * cnt[a[i]] * j;
        }
        ans += cnt[a[i]] * (cnt[a[i]] - 1) / 2;
    }
    cout << ans << '\n';
}
posted @   lightmon  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示