• APIO 2019 路灯


    题目链接

    LOJ3146
    LuoguP5445

    思路要点

    我们称能够相互到达的子段为关键段。

    维护关键段可以用线段树维护。

    先考虑断开某一个关键段 ([l,r]) 的某一条边 (k),那么这个操作只对 ([l,k])([k+1,r]) 内的答案有贡献。同理加入这条边也只对这两个关键段有贡献。

    考虑将这个贡献差分,就得到了:

    • 若插入,在第 (i) 个时刻,([l,k])([k+1,r]) 内的答案 (-i)
    • 若删除,在第 (i) 个时刻,([l,k])([k+1,r]) 内的答案 (+i)

    一个特判,如果每个询问的最后时刻 (t),若这两个点是联通的,那么我们需要 (+t)

    接下来,我们将两个关键段抽象成二维坐标系内的 (x) 轴和 (y) 轴,也就是每次在 ((l,k+1,k,r)) 内的矩形进行带权修改。

    因此考虑离线分治计算答案,是一个二维数点问题。

    时间复杂度 (Theta (nlog^2n))

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    
    const int N = 3e5 + 5;
    
    char s[N];
    int n, q, etot;
    int a[N], ans[N];
    bool vis[N];
    
    struct Node {
      pair<int, int> bd;
      bool tag;
    } tr[N << 2];
    
    struct Op {
      int t, x1, y1, x2, y2, id, v;
    } op[N];
    
    struct Event {
      int x, ys, ye, v;
    
      bool operator<(const Event& rhs) const {
        return x < rhs.x;
      }
    } evt[N << 1];
    
    int fw[N];
    
    void pushdown(int x) {
      if (tr[x].tag) {
        tr[x << 1].bd = tr[x << 1 | 1].bd = tr[x].bd;
        tr[x << 1].tag = tr[x << 1 | 1].tag = 1;
        tr[x].tag = 0;
      }
    }
    
    void build(int x, int l, int r) {
      if (l == r) {
        tr[x].bd = make_pair(l, l);
        return;
      }
      int mid = (l + r) >> 1;
      build(x << 1, l, mid);
      build(x << 1 | 1, mid + 1, r);
    }
    
    void modify(int x, int l, int r, int ql, int qr, pair<int, int> val) {
      if (ql <= l && r <= qr) {
        tr[x].bd = val;
        tr[x].tag = 1;
        return;
      }
      pushdown(x);
      int mid = (l + r) >> 1;
      if (ql <= mid)
        modify(x << 1, l, mid, ql, qr, val);
      if (qr > mid)
        modify(x << 1 | 1, mid + 1, r, ql, qr, val);
    }
    
    pair<int, int> query(int x, int l, int r, int p) {
      if (l == r) 
        return tr[x].bd;
      pushdown(x);
      int mid = (l + r) >> 1;
      if (p <= mid) 
        return query(x << 1, l, mid, p);
      else 
        return query(x << 1 | 1, mid + 1, r, p);
    }
    
    void add(int p, int v) {
      for (int i = p; i <= n + 1; i += i & -i)
        fw[i] += v; 
    }
    
    int query(int p) {
      int ans = 0; 
      for (int i = p; i >= 1; i -= i & -i)
        ans += fw[i];
      return ans;
    }
    
    void addEvent(int x, int ys, int ye, int v) {
      evt[++etot] = (Event){x, ys, ye, v};
    }
    
    void solve(int l, int r) {
      if (l == r)
        return;
      int mid = (l + r) >> 1;
      solve(l, mid), solve(mid + 1, r);
      etot = 0;
      for (int i = l; i <= mid; i++)
        if (op[i].t == 1) {
          addEvent(op[i].x1, op[i].y1, op[i].y2, op[i].v);
          addEvent(op[i].x2 + 1, op[i].y1, op[i].y2, -op[i].v);
        }
      sort(evt + 1, evt + 1 + etot);
      sort(op + mid + 1, op + 1 + r, [](Op a, Op b) {
        return a.x1 < b.x1;
      });
      int pt = 1; 
      for (int i = mid + 1; i <= r; i++)
        if (op[i].t == 2) {
          while (pt <= etot && evt[pt].x <= op[i].x1) {
            add(evt[pt].ys, evt[pt].v);
            add(evt[pt].ye + 1, -evt[pt].v);
            pt++;
          }
          ans[op[i].id] += query(op[i].y1);
        }
      for (int i = 1; i < pt; i++) {
        add(evt[i].ys, -evt[i].v);
        add(evt[i].ye + 1, evt[i].v);
      }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("input.in", "r", stdin);
      freopen("output.out", "w", stdout);
    #endif
      scanf("%d %d", &n, &q);
      scanf("%s", s);
      for (int i = 1; i <= n; i++)
        a[i] = s[i - 1] - '0';
      build(1, 1, n + 1);
      for (int i = 1; i <= n; i++) 
        if (a[i] == 1 && !vis[i]) {
          int it = i;
          vis[i] = 1; 
          while (a[it]) 
            it++, vis[it] = 1; 
          modify(1, 1, n + 1, i, it, make_pair(i, it));
        }
      memset(ans, 0, sizeof ans);
      memset(vis, 0, sizeof vis);
      char opt[10];
      for (int i = 1; i <= q; i++) {
        scanf("%s", opt);
        op[i].t = (opt[0] == 'q' ? 2 : 1);
        op[i].id = i; 
        if (op[i].t == 1) {
          int k; 
          scanf("%d", &k);
          if (a[k] == 1) {
            pair<int, int> p = query(1, 1, n + 1, k);
            op[i].x1 = p.first, op[i].x2 = k;
            op[i].y1 = k + 1, op[i].y2 = p.second;
            op[i].v = i; 
            modify(1, 1, n + 1, p.first, k, make_pair(p.first, k));
            modify(1, 1, n + 1, k + 1, p.second, make_pair(k + 1, p.second));
          } else {
            pair<int, int> p1 = query(1, 1, n + 1, k), p2 = query(1, 1, n + 1, k + 1);
            op[i].x1 = p1.first, op[i].x2 = k; 
            op[i].y1 = k + 1, op[i].y2 = p2.second;
            op[i].v = -i;
            modify(1, 1, n + 1, p1.first, p2.second, make_pair(p1.first, p2.second));
          }
          a[k] ^= 1;
        } else {
          scanf("%d %d", &op[i].x1, &op[i].y1);
          pair<int, int> p = query(1, 1, n + 1, op[i].x1);
          if (p.second >= op[i].y1 && p.first <= op[i].y1) 
            ans[i] += i; 
          vis[i] = 1; 
        }
      }
      memset(fw, 0, sizeof fw);
      solve(1, q);
      for (int i = 1; i <= q; i++)
        if (vis[i])
          printf("%d
    ", ans[i]);
      return 0;
    }
    

    后言

    • 这道题目充分的运用了数形结合和差分的思想,这可以将每一个操作对整体答案的贡献独立处理。而且更加形象的理解。
  • 相关阅读:
    laravel windows下安装 gulp 和 laravel-elixir
    php-新特性,生成器的创建和使用
    laravel 使用极验验证码
    laravel 发送邮件
    laravel安装 redis 并驱动 session
    理解HTTP协议(转载)
    iOS中Block的用法,举例,解析与底层原理
    iOS自定义结构体
    dyld环境变量
    iOS中的静态库与动态库,区别、制作和使用
  • 原文地址:https://www.cnblogs.com/chhokmah/p/12624338.html
Copyright © 2020-2023  润新知