1 条题解

  • 0
    @ 2026-4-23 17:10:27

    没有人类了。

    由于数据量极大,下文讨论中认为 __builtin_popcount 函数的时间复杂度为 O(loglogV)\mathcal{O}(\log\log V)

    O(TlogVloglogV)\mathcal{O}(T\log V\log\log V)

    不难发现你给 yy 乘二纯粹是闲着没事干。所以我们要干的事情是将 xx 一直倍增,使其尽可能贴近 yy。分为两种情况:

    1. 倍增了 tt 次,2tx>y2^t x>y。此时操作次数为 t+2txyt+2^tx-y
    2. 倍增了 tt 次,2txy2^t x\le y,还需要补上 k=y2kk=y-2^k

    显然第一类要选尽可能小的 tt,第二类要选尽可能大的。这个可以用 __lg 算。问题在于,在第二类操作中,答案并不是简单的 kk11 的个数,我们可以搞出一个比 kk 大的数再减回去。枚举减了几次可以得到这部分代价为:

    $$\min_i\left(i+\left\lfloor\frac{k+i}{2^t}\right\rfloor+\operatorname{popcount}((k+i)\bmod2^t)\right)$$

    ii 超过 logV\log V 必然不优,因此暴力计算即可,期望得分 8484

    O(Tlog2logV)\mathcal{O}(T\log^2\log V)

    直接枚举 ii 还是太菜了。这是因为一次次 +1+1 浪费了太多步数。我们的目标是要减少 popcount,所以可以改成每次加 lowbit,这样只会加 O(loglogV)\mathcal{O}(\log\log V) 次,期望得分 9696

    O(logVloglogV+TloglogV)\mathcal{O}(\log V\log\log V+T\log\log V)

    不妨再将 ii 分为两类:

    1. 我们的目标比较长远,要把很长一段 11 填平。这个时候把最低 66 位直接填平一定是不劣的。
    2. 没那么多 11 让我们消,那么我们只关心最后 66 位内的情况。这部分可以预处理出对应的答案。

    第一类直接位运算解决,第二类提取低六位查表即可。期望得分 100100

    #include <bits/stdc++.h>
    #include "binary.h"
    
    using namespace std;
    
    typedef long long ll;
    
    int f[64], pc[70];
    
    void init(int c, int t) {
    	for (int i = 0; i < 70; i++) pc[i] = __builtin_popcount(i);
    	for (int i = 0; i < 64; i++) {
    		for (int j = 0; j < 6; j++) f[i] = max(f[i], pc[i] - pc[i + j] - j);
    	}
    }
    
    inline 
    int calc(ll x) {
    	if (!x) return 0; ll y = (x | 63) + 1;
    	return min<int>(__builtin_popcountll(y) + y - x, __builtin_popcountll(x) - f[x & 63]);
    }
    
    ll binary(ll x, ll y) {
    	int cnt = __lg(y) - __lg(x); x <<= cnt; if (x > y) x >>= 1, cnt--;
    	return cnt + min<ll>((x << 1) - y + 1, (y - x >> cnt) + calc(y - x & (1ll << cnt) - 1));
    }
    

    Bonus:O(V3+logVloglogV+T)\mathcal{O}(\sqrt[3]V+\log V\log\log V+T)

    额就是你打表一下 popcount,就这样。但是寻址三次和 popcount 哪个快我确实不好说。这个可以做到理论最优就是了。

    #include <bits/stdc++.h>
    #include "binary.h"
    
    using namespace std;
    
    typedef long long ll;
    
    const int B = 1 << 21;
    
    int f[64], pc[1 << 21];
    
    void init(int c, int t) {
    	for (int i = 1; i < B; i++) pc[i] = pc[i & i - 1] + 1;
    	for (int i = 0; i < 64; i++) {
    		for (int j = 0; j < 6; j++) f[i] = max(f[i], pc[i] - pc[i + j] - j);
    	}
    }
    
    inline int popcount(ll x) { return pc[x & B - 1] + pc[x >> 21 & B - 1] + pc[x >> 42 & B - 1]; }
    
    inline 
    int calc(ll x) {
    	if (!x) return 0; ll y = (x | 63) + 1;
    	return min<int>(popcount(y) + y - x, popcount(x) - f[x & 63]);
    }
    
    ll binary(ll x, ll y) {
    	int cnt = __lg(y) - __lg(x); x <<= cnt; if (x > y) x >>= 1, cnt--;
    	return cnt + min<ll>((x << 1) - y + 1, (y - x >> cnt) + calc(y - x & (1ll << cnt) - 1));
    }
    
    • 1

    信息

    ID
    7199
    时间
    3000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者