• Solution 「ROI 2018」「LOJ #2850」无进位加法


    \(\mathscr{Description}\)

      Link.

      给定 \(\{a_n\}\),每次令某个 \(a_i\leftarrow a_i+1\),最终得到 \(\{b_n\}\),求 \(\sum b=\bigoplus b\)\(\sum b\) 的最小值。

      \(n,\sum\lceil\log_2a_i\rceil\le3\times10^5\)

    \(\mathscr{Solution}\)

      很有贪心的味道,我们从 \(a_i=2^{k_i}\) 的情况入手分析。在这一情况下,贪心是平凡的:从低到高扫 bit,当前 bit 最多留一个数,其他的进位。考察此时答案的最高 bit \(h\),可以发现,当 \(\{a_i\}\) 降序排列时,有 \(h=\max_{i=1}^n\{k_i+i-1\}\)。必要性显然,归纳可证充分。

      同时,注意到一种简单的规约方式:若 \(a_i\in[2^{k_i},2^{k_i+1})\),令所有 \(a_i\) 二进制上取整,规约到上面这种情况,此时答案的最高 bit 为 \(h+1\)。因此我们可以得到一个思路:求出 \(h\),判断答案最高 bit 能否为 \(h\)

      判断最高 bit?暴力一点,直接搜。设 \(p=\min\arg\max_{i=1}^n\{k_i+i-1\}\),那么答案 \([h-p+2,h]\) 里的 bit 全为 \(1\)(它们把 \([1,p)\) 里的数一一抬走,这些数也应当被删掉),如果 \(h\) 不行,那 \(p\) 就必然被 \(h+1\) 抬走;否则 \(h-p+1\) 可以让 \(a_p\) 变小但无法把它抬走(它是 \(\arg\max\)),怎么办?直接将 \(a_p-2^{h-p+1}\),也即是 \(a_p\) 丢到最高位得到的 \(a_p'\),放回集合里,递归判断最高 bit 能否为 \(h-p\)

      实现上,每个数以 \(1\) 位最高 bit 的后缀才可能被加入 \(\{a_n\}\),所以可以在外部对所有这样的后缀基排,把 \(x\) 加入集合时,在线段树 \(x\) “绝对排名”的位置上做修改。由于线段树只是拿来维护 \(\max\)\(\arg\max\),所以当前不在集合里的数对应排名位置上的值设成极小就好。

      然后——既然你发现我都在将实现了——设 \(L=\sum\lceil\log_2a_i\rceil\),我们宣称这个算法的复杂度是 \(\mathcal O((n+L)\log L)\) 的,直接写就完啦。

    证明   考虑搜索过程,由于最后能得到答案,所以必然有 $\mathcal O(n)$ 次成功的递归,这部分复杂度是 $\mathcal O(n\log L)$。

      关键是回溯的复杂度,在此先描述一下具体一点的算法流程。对于当前的 \(h\),若不能直接选,我们将尝试 \(h+1\)。若 \(h+1\) 已经超过了要求的答案上界,就需要把 \([1,p)\) 里的数全部加回来;否则 \(h+1\) 一旦出手,必然出解(已经选出理论上界了),不用回溯。

      对着代码进行神妙观察, 我们发现:每个回溯的 \(x\) 都唯一对应这答案里一个确定为 \(1\) 的 bit(来自上文“……里的 bit 全为 \(1\)” 那句话),答案 bit 数显然不超过 \(\mathcal O((n+L)\log L)\)!因此,回溯的总复杂度为 \(\mathcal O((n+L)\log L)\),算法复杂度亦为此。 \(\square\)

    \(\mathscr{Code}\)

    /*+Rainybunny+*/
    
    #include <bits/stdc++.h>
    
    #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
    #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
    
    typedef std::pair<int, int> PII;
    #define fi first
    #define se second
    
    const int MAXN = 3e5, IINF = 0x3f3f3f3f;
    int n, mxl, tot, low[MAXN + 5], ord[MAXN + 5];
    std::vector<int> dgt[MAXN + 5], rnk[MAXN + 5], own[MAXN + 5];
    PII ref[MAXN + 5];
    std::set<int> alv;
    bool ans[MAXN * 2 + 5];
    
    struct SegmentTree {
        PII mx[MAXN << 2];
        int tag[MAXN << 2];
    
        inline void pushup(const int u) {
            mx[u] = std::max(mx[u << 1], mx[u << 1 | 1]);
        }
    
        inline void pushad(const int u, const int k) {
            mx[u].fi += k, tag[u] += k;
        }
    
        inline void pushdn(const int u) {
            if (tag[u]) {
                pushad(u << 1, tag[u]), pushad(u << 1 | 1, tag[u]);
                tag[u] = 0;
            }
        }
    
        inline void build(const int u, const int  l, const int r) {
            mx[u] = { -IINF, l };
            if (l == r) return ;
            int mid = l + r >> 1;
            build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        }
    
        inline void modify(const int u, const int l, const int r,
          const int ml, const int mr, const int k) {
            if (ml > mr) return ;
            if (ml <= l && r <= mr) return pushad(u, k);
            int mid = l + r >> 1; pushdn(u);
            if (ml <= mid) modify(u << 1, l, mid, ml, mr, k);
            if (mid < mr) modify(u << 1 | 1, mid + 1, r, ml, mr, k);
            pushup(u);
        }
    } sgt;
    
    inline void update(const int rk, const int type) {
        if (~type) alv.insert(rk);
        else alv.erase(rk);
        sgt.modify(1, 1, tot, rk, rk, type * (IINF + dgt[ref[rk].fi][ref[rk].se]));
        sgt.modify(1, 1, tot, 1, rk - 1, type);
    }
    
    inline bool solve(const int lim) {
        if (sgt.mx[1].fi < 0) return true;
        if (sgt.mx[1].fi >= lim) return false;
        int tar = sgt.mx[1].fi, rk = sgt.mx[1].se; PII id = ref[rk];
    
        update(rk, -1);
        if (id.se) update(rnk[id.fi][id.se - 1], 1);
    
        std::vector<int> back;
        while (alv.size() && *alv.rbegin() > rk) {
            back.push_back(*alv.rbegin()), update(*alv.rbegin(), -1);
        }
    
        rep (i, 0, back.size()) ans[tar - i] = true;
        if (solve(tar - back.size())) return true;
    
        ans[tar - back.size()] = false;
        if (id.se) update(rnk[id.fi][id.se - 1], -1);
        if (tar + 1 < lim) {
            ans[tar + 1] = true;
            assert(solve(tar - back.size() + 1));
            return true;
        } else {
            update(rk, 1);
            for (int u: back) update(u, 1);
            return false;
        }
    }
    
    int main() {
        std::ios::sync_with_stdio(false), std::cin.tie(0);
    
        std::cin >> n;
        rep (i, 1, n) {
            static std::string str; std::cin >> str;
            std::reverse(str.begin(), str.end());
            int len = str.size(); mxl = std::max(mxl, len);
            rep (j, 0, len - 1) if (str[j] == '1') {
                dgt[i].push_back(j), own[j].push_back(i);
            }
        }
        rep (i, 0, mxl - 1) {
            ord[0] = 0;
            for (int j: own[i]) ord[++ord[0]] = j;
            std::sort(ord + 1, ord + ord[0] + 1,
              [](const int u, const int v) { return low[u] < low[v]; });
            rep (j, 1, ord[0]) {
                rnk[ord[j]].push_back(low[ord[j]] = ++tot);
                ref[tot] = { ord[j], rnk[ord[j]].size() - 1 };
            }
        }
    
        sgt.build(1, 1, tot);
        rep (i, 1, n) update(rnk[i].back(), 1);
    
        assert(solve(IINF));
    
        int fin = 0;
        rep (i, 0, mxl + n + 1) if (ans[i]) fin = i;
        per (i, fin, 0) std::cout << ans[i];
        std::cout << '\n';
        return 0;
    }
    
    
  • 相关阅读:
    SharePoint与RMS集成中关于权限的一个技术点
    SharePoint Alert
    SharePoint Explorer View
    在查看network traffic的时候, TCP Chimney offload的影响
    SharePoint Profile Import
    为SharePoint添加Event Receiver
    通过Telnet来发送邮件
    如何查看扩展出来的web application?
    Windows Host 文件
    Wscript.Shell 对象详细介绍
  • 原文地址:https://www.cnblogs.com/rainybunny/p/16074205.html
Copyright © 2020-2023  润新知