AT_abc282_f

发布时间 2023-06-05 22:43:44作者: shinzanmono

题意:

让你你给出交互库一些数对 $(l_i,r_i)(1\leq l_i\leq r_i\leq n)$,不得大于 $50000$ 个,每次询问给出两个正整数 $L,R$,请选出两个数对 $(l_i,r_i), (l_j,r_j)$ 使得 $[l_i,r_i]\cup[l_j,r_j]=[L,R]$。

Solution:

考虑数据范围:$1\leq n\leq4000$,如果考虑枚举所有满足条件的数对,那么一共有 $\frac{n(n+1)}2>50000$,显然不满足题意。

稍加计算,发现 $n\log_2n$ 的最大值和 $50000$ 比较相近,所以我们考虑倍增算法。

我们来想一下 ST 表,ST 表的原理是:$f_{i,j}$ 维护了区间 $[j,j+2^i-1]$ 的 $\operatorname{op}$ 值($\operatorname{op}$ 指一种操作) 当查询区间 $[l,r]$ 的时候,只需要查询 $\operatorname{op}(f_{\lfloor \log_2(r-l+1)\rfloor,l},f_{\lfloor\log_2(r-l+1)\rfloor,r-2^{\lfloor\log_2(r-l+1)\rfloor}+1})$。

这题时需要你求出两个区间是的两个区间的并是第三个区间,我们可以像 ST 表一样,对于每一个 $i$ 满足 $1\leq i\leq n$ 求出所有的 $j,j+2^k-1$,然后对于每个查询操作,令 $k=\lfloor \log_2(R-L+1)\rfloor$,找出区间 $[L,L+2^k-1]$ 与 $[R-2^k+1,R]$,输出编号即可。

CODE:

#include<iostream>
#include<map>
std::map<std::pair<int, int>, int> dict;
int main() {
    std::ios::sync_with_stdio(false);
    int n;
    std::cin >> n;
    int id = 0;
    for (int j = 0; j <= std::__lg(n); j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            dict[std::make_pair(i, i + (1 << j) - 1)] = ++id; // 记录每个区间的编号
    std::cout << id << std::endl; // 输出区间的个数
    for (int j = 0; j <= std::__lg(n); j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            std::cout << i << " " << i + (1 << j) - 1 << std::endl; // 输出区间
    int q;
    std::cin >> q;
    while (q--) {
        int l, r;
        std::cin >> l >> r;
        int lg = std::__lg(r - l + 1); 
        std::cout << dict[std::make_pair(l, l + (1 << lg) - 1)] << " "
                  << dict[std::make_pair(r - (1 << lg) + 1, r)] << std::endl; // 查找区间
    }
    return 0; // 完结撒花
}

注意:这是一道交互题,别忘了输出后清空缓冲区。