比赛链接:Here

ABC水题,

D - Cutting Woods

题意:开始一根木棒长度为 AtCoder Beginner Contest 217 D~E - 图1 并以 AtCoder Beginner Contest 217 D~E - 图2 为单位在木棒上标记AtCoder Beginner Contest 217 D~E - 图3#card=math&code=%281%5Csim%20n%29) ,输出 AtCoder Beginner Contest 217 D~E - 图4 次操作

  • 操作 AtCoder Beginner Contest 217 D~E - 图5 断开 AtCoder Beginner Contest 217 D~E - 图6 所在的木棒:AtCoder Beginner Contest 217 D~E - 图7AtCoder Beginner Contest 217 D~E - 图8 断开变成了 AtCoder Beginner Contest 217 D~E - 图9
  • 操作 AtCoder Beginner Contest 217 D~E - 图10 查询 AtCoder Beginner Contest 217 D~E - 图11 所在区间的长度

数据范围:AtCoder Beginner Contest 217 D~E - 图12

题解:

一开始没有想到这个性质所以卡住了,

在分割之后左边以及右边这个区间的答案是固定的,也就是说答案只跟分割点有关,比如区间 AtCoder Beginner Contest 217 D~E - 图13 分割 AtCoder Beginner Contest 217 D~E - 图14 变成了 AtCoder Beginner Contest 217 D~E - 图15

可以发现可以发现
AtCoder Beginner Contest 217 D~E - 图16AtCoder Beginner Contest 217 D~E - 图17 这个区间的询问答案都是 AtCoder Beginner Contest 217 D~E - 图18
AtCoder Beginner Contest 217 D~E - 图19AtCoder Beginner Contest 217 D~E - 图20 这个区间的询问答案都是 AtCoder Beginner Contest 217 D~E - 图21

所以可以考虑二分找到所在区间相邻 AtCoder Beginner Contest 217 D~E - 图22 个的分割点下标

先考虑边界问题

边界无非是:AtCoder Beginner Contest 217 D~E - 图23AtCoder Beginner Contest 217 D~E - 图24 种的其中一种

这个时候先假设区间是 AtCoder Beginner Contest 217 D~E - 图25

分割点是 0/1 2 4 5/5+1

对于查询2
找到第一个大于等于2的数是2
第一个小于2的数应该是 0/1
答案是2
所以应该是 2 - 0 = 2
左边界应该是0

在考虑查询5
答案是1
找到第一个大于等于5的数是5/6
第一个小于5的数应该是4
所以应该是 5 - 4 = 1
右边界是n

在考虑二分操作的时候 分割点数组保持有序
所以可以用set动态维护

时间复杂度:AtCoder Beginner Contest 217 D~E - 图26#card=math&code=%5Cmathcal%7BO%7D%28Nlog%20N%29)

  1. set<int>s;
  2. int main() {
  3. ios::sync_with_stdio(false), cin.tie(nullptr);
  4. int n, m;
  5. cin >> n >> m;
  6. s.insert(0);
  7. s.insert(n);
  8. while (m--) {
  9. int c, x;
  10. cin >> c >> x;
  11. if (c == 1) s.insert(x);
  12. else
  13. cout << *s.lower_bound(x) - *--s.upper_bound(x) << "\n";
  14. // 因为没 -- 调了半天
  15. }
  16. }

E - Sorting Queries

题意:给出一个空的序列和 AtCoder Beginner Contest 217 D~E - 图27 次操作

  • 操作 AtCoder Beginner Contest 217 D~E - 图28: 在序列末尾添加一个数 AtCoder Beginner Contest 217 D~E - 图29
  • 操作 AtCoder Beginner Contest 217 D~E - 图30 :输出序列第一个数
  • 操作 AtCoder Beginner Contest 217 D~E - 图31:给当前序列排序

数据范围:AtCoder Beginner Contest 217 D~E - 图32

题解:

假设不考虑操作 AtCoder Beginner Contest 217 D~E - 图33,那么我们可直接模拟,

但在操作 AtCoder Beginner Contest 217 D~E - 图34的影响下,需要维护排序对队列的影响,

假设队列中两个元素,AtCoder Beginner Contest 217 D~E - 图35 对应队头和队尾

暴力时间复杂度是 AtCoder Beginner Contest 217 D~E - 图36#card=math&code=%5Cmathcal%7BO%7D%28n%5E2%20logn%29) 肯定是不可取的

在排序的时候我们可以考虑边插入边排序,用 AtCoder Beginner Contest 217 D~E - 图37 维护

然后把对头指向 AtCoder Beginner Contest 217 D~E - 图38 后面的第一个下标

这样时间复杂度便降到了 AtCoder Beginner Contest 217 D~E - 图39#card=math&code=%5Cmathcal%7BO%7D%28n%20logn%29)

const int N = 1e6 + 10;
ll a[N];
multiset<ll>s;
bool st[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int head = 0, fail = 0;
    int t; cin >> t;
    int k = 1; // k = 1 表示队列第一个数的下标是1
    while (t--) {
        int op; cin >> op;
        if (op == 1) {
            int x; cin >> x;
            a[++fail] = x;
        } else if (op == 2) {
            // 如果排序过
            if (s.size()) {
                cout << *s.begin() << "\n" ;
                s.erase(s.begin());
            } else {
                cout << a[k] << "\n";
                st[k] = 1;
                k += 1;
            }
        } else {
            for (int i = k ; i <= fail ; i ++) {
                if (!st[i]) s.insert(a[i]) ;
            }
            // 插入之后 对头指向队尾的下一个下标
            k = fail + 1 ;
        }
    }
}