题解 | 牛客周赛 Round 160

A 小月的开关

知识点:取模、循环状态

状态按照 $3$ 个值循环,按一次按钮就是在当前状态上加 $1$ 后对 $3$ 取模,因此直接输出

$$(x+1)\bmod 3.$$

时间复杂度 $\mathcal{O}(1)$。

void solve() {
    int x;
    cin >> x;
    cout << (x + 1) % 3 << endl;
}

B 小月的彩灯

知识点:位运算、循环移位、二进制

环的长度是 $4$,所以移动次数只需要保留 $k\bmod 4$。为了书写方便,左移 $k$ 次其实等价于右移 $4 – k$ 次(模意义下取正):每次取出最低位,整体右移一位,再把取出的位放回第 $3$ 位。

统计 $y$ 的二进制表示中 $1$ 的个数即可。

时间复杂度 $\mathcal{O}(1)$。

void solve() {
    int x, k;
    cin >> x >> k;
    k = ((4 - k) % 4 + 4) % 4;
    while (k--) {
        bool f = x & 1;
        x >>= 1;
        if (f) x |= 1 << 3;
    }
    cout << x << " " << __builtin_popcountll(x) << endl;
}

C 小月的平方刻度

知识点:整数平方根、公式计算

$$r=\left\lfloor\sqrt{x}\right\rfloor.$$

那么 $r^2$ 是不超过 $x$ 的最大完全平方数,两个距离分别为

$$d_{\mathrm{low}}=x-r^2,\qquad d_{\mathrm{high}}=(r+1)^2-x.$$

用长双精度函数 $\texttt{sqrtl}$ 求出 $r$,再直接代入上式即可。展开了是因为我觉得打括号很别手。

时间复杂度 $\mathcal{O}(1)$。

void solve() {
    int x;
    cin >> x;
    int r = sqrtl(x);
    cout << r << " " << x - r * r << " " << r * r + 2 * r + 1 - x << endl;
}

D 小月的计数器

知识点:二进制进位、数位统计

不难发现,如果 $i$ 的末尾有 $k$ 个 $0$ ,那么从 $i-1$ 的末尾就有 $k$ 个 $1$ 。加 $1$ 时,末尾的 $k$ 个 $1$ 变成 $0$ ,它前面的 $0$ 变成 $1$ ,所以总共会有
$$1+\nu_2(i)$$
次翻页,其中 $\nu_2(i)$ 表示二进制末尾连续 $0$ 的个数。

所以总答案为
$$S(n) = \sum\limits_{i=1}^{n}(1+\nu_2(i)) = n + \sum_{i=1}^{n}\nu_2(i)$$
一个数的 $\nu_2(i)$ 之和,同时也可以表示为 $n$ 能被多少个 $2^k$ 整除,所以可以交换求和顺序
$$\sum_{i=1}^{n}\nu_2(i) = \sum_{k\geqq1}\lfloor\frac{n}{2^k}\rfloor$$
我们将 $n$ 写成二进制,可以表示为 $n = \sum\limits_{j\geqq 0}b_j2^j,,b_j\in{0,1}$ ,所以有
$$\begin{align}\sum_{k\geqq1}\lfloor\frac{n}{2^k}\rfloor &= \sum\limits_{j\geqq 0}b_j\sum_{k=1}^j2^{j-k}\\&=\sum_{j\geqq 0}b_j(2^j-1)\\&=n-\sum_{j\geqq 0}b_j\\&=n-\operatorname{popcount}(n)\end{align}$$
因此答案为
$$2n – \operatorname{popcount}(n)$$

时间复杂度 $\mathcal{O}(1)$。

void solve() {
    int n;
    cin >> n;
    cout << 2 * n - __builtin_popcountll(n) << endl;
}

E 小月的相交弦

知识点:区间 DP、端点配对、非相交结构

记端点总数为 $m=2n$,令 $\texttt{dp}[l][r]$ 表示只考虑端点区间 $[l,r]$ 内、且两端都在该区间中的弦时,能够取得的最大权值。处理右端点 $r$,设它配对的另一个端点为 $p$。

若不选弦 $(p,r)$,答案就是 $\texttt{dp}[l][r-1]$。若选择它且 $l\le p<r$,任何一个被选弦都不能用一个端点落在 $[l,p-1]$、另一个端点落在 $[p+1,r-1]$,否则会与 $(p,r)$ 相交;因此两侧可以独立求解,转移为

$$\begin{aligned}\texttt{dp}[l][r]=\max\bigl(&\textrm{dp}[l][r-1], \textrm{dp}[l][p-1]+\textrm{dp}[p+1][r-1]+w_r\bigr).\\\end{aligned}$$

嵌套关系不会产生冲突,所以区间拆分已经覆盖所有合法选择;空区间的值取 $0$,也自然处理负权弦。

时间复杂度 $\mathcal{O}(n^2)$。

void solve() {
    int n;
    cin >> n;
    int m = 2 * n;
    vector<int> to(m + 1), w(m + 1);
    for (int i = 0; i < n; ++i) {
        int l, r, x;
        cin >> l >> r >> x;
        to[l] = r;
        to[r] = l;
        w[l] = w[r] = x;
    }

    vector<vector<int>> dp(m + 1, vector<int>(m + 1));
    auto get = [&](int l, int r) -> int {
        if (l > r) return 0;
        return dp[l][r - l];
    };

    for (int len = 1; len <= m; ++len) {
        for (int l = 1; l + len - 1 <= m; ++l) {
            int r = l + len - 1, p = to[r];
            int cur = get(l, r - 1);
            if (l <= p && p < r)
                cur = max(cur, get(l, p - 1) + get(p + 1, r - 1) + w[r]);
            dp[l][r - l] = cur;
        }
    }
    cout << get(1, m) << endl;
}

F 小月的能量带

知识点:排序、树状数组、坐标压缩、区间计数

按 $(l,r,\texttt{id})$ 排序后,设两条区间为 $A=[l_1,r_1)$、$B=[l_2,r_2)$,且 $l_1\le l_2$。并集连续的条件是 $l_2\le r_1$。

若 $r_2\le r_1$,并集长度为 $r_1-l_1$,所以必须有 $r_1-l_1=t$;这正是一个长度为 $t$ 的区间包含另一个区间的情况。逆序扫描排序后的区间,对每个长度为 $t$ 的区间统计后方且右端点不超过 $r_1$ 的区间。把所有右端点离散化后,用树状数组维护出现次数即可。排序保证后方区间的左端点不会小于 $l_1$;同左端点但更短的区间排在前面,留到下面的情况处理。

若 $r_2>r_1$,则并集长度为 $r_2-l_1$,必须满足

$$r_2=l_1+t,\qquad l_2\le r_1.$$

此时当前区间长度必小于 $t$。按右端点把区间分组,并在每组中按 $(l,\texttt{id})$ 排序;对当前区间 $i$,只需二分统计右端点为 $l_i+t$、满足 $(l_j,\texttt{id}_j)>(l_i,\texttt{id}_i)$ 且 $l_j\le r_i$ 的候选。严格的排序键保证无序区间对只被计数一次,也覆盖了两个区间左端点相同的边界。

上述两类已经穷尽了并集长度为 $t$ 的连续区间对。

时间复杂度 $\mathcal{O}(n\log n)$。

constexpr int inf = 2e18 + 9;

template <class T> struct BIT {...}; // 树状数组

void solve() {
    int n, t;
    cin >> n >> t;
    vector<array<int, 3>> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i][0] >> a[i][1];
        a[i][2] = i;
    }
    sort(a.begin(), a.end());

    vector<int> v;
    for (auto x : a) v.push_back(x[1]);
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());

    BIT<int> tr(v.size());

    int ans = 0;
    for (int i = n - 1; i >= 0; --i) {
        if (a[i][1] - a[i][0] == t) {
            int p = upper_bound(v.begin(), v.end(), a[i][1]) - v.begin();
            ans += tr.ask(p);
        }
        int p = lower_bound(v.begin(), v.end(), a[i][1]) - v.begin() + 1;
        tr.modify(p, 1);
    }

    map<int, vector<pii>> mp;
    for (int i = 0; i < n; ++i) mp[a[i][1]].push_back({a[i][0], i});
    for (int i = 0; i < n; ++i) {
        int l = a[i][0], r = a[i][1];
        if (r - l >= t) continue;
        auto it = mp.find(l + t);
        if (it == mp.end()) continue;
        auto &b = it->second;
        int x = upper_bound(b.begin(), b.end(), pii{l, i}) - b.begin();
        int y = upper_bound(b.begin(), b.end(), pii{r, inf}) - b.begin();
        ans += y - x;
    }
    cout << ans << endl;
}

约定

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define pii array<int, 2>
#define endl "\n"

void solve() {
    
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout << fixed << setprecision(20);
    int t = 1;
    cin >> t;
    for (int i = 0; i < t; ++i) {
        solve();
    }
    return 0;
}

使用到的算法模板见 $\texttt{github}$ 仓库 林月的XCPC模板库

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇