• [bzoj3995][SDOI2015]道路修建-线段树


    Brief Description

    某国有2N个城市,这2N个城市构成了一个2行N列的方格网。现在该国政府有一个旅游发展计划,这个计划需要选定L、R两列(L<=R),修建若干条专用道路,使得这两列之间(包括这两列)的所有2(R-L+1)个城市中每个城市可以只通过专用道路就可以到达这2(R-L+1)个城市中的任何一个城市。这种专用道路只能在同一行相邻两列的城市或者同一列的两个城市之间修建,且修建需要花费一定的费用。由于该国政府决定尽量缩减开支,因此政府决定,选定L、R后,只修建2(R-L+1)-1条专用道路,使得这些专用道路构成一个树结构。现在你需要帮助该国政府写一个程序,完成这个任务。具体地,该任务包含M个操作,每个操作的格式如下:

    1.    C x0 y0 x1 y1 w:由于重新对第x0行第y0列的城市和第x1行第y1列的城市之间的情况进行了考察,它们之间修建一条专用道路的花费变成了w;
      
    2.    Q L R:若政府选定的两列分别为L、R,询问政府的最小开支。
      

    Algorithm Design

    我们考虑使用线段树维护区间([l,r])的最小生成树.
    考察区间合并:
    可以发现, 区间合并时一共有五种本质不同的情况:

    其中相同颜色的是同一个联通块.
    然后合并的时候有两种选择, 一种是加一条边, 一种是加两条边, 枚举所有合并情况就好了, 维护这些所有的状态.
    其实这个题目和SHOI某年一个题非常相似.

    Code

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    const int maxn = 60110;
    int l[maxn][2], r[maxn][2], u[maxn], d[maxn];
    int N, M;
    struct seg {
      int a[5];
      int l, r;
    } t[maxn << 2];
    void up(int &a, int b) { a = std::min(a, b); }
    void update(int a[5], int l[5], int r[5], int se, int de) {
      memset(a, 0x3f, sizeof(t[0].a));
    
      up(a[0], l[0] + r[0] + se);
      up(a[1], l[0] + r[1] + se);
      up(a[0], l[0] + r[2] + de);
      up(a[0], l[0] + r[3] + de);
      up(a[1], l[0] + r[3] + se);
      up(a[1], l[0] + r[4] + de);
    
      up(a[0], l[1] + r[0] + de);
      up(a[1], l[1] + r[1] + de);
      up(a[1], l[1] + r[3] + de);
    
      up(a[2], l[2] + r[0] + se);
      up(a[4], l[2] + r[1] + se);
      up(a[2], l[2] + r[2] + de);
      up(a[4], l[2] + r[3] + se);
      up(a[2], l[2] + r[3] + de);
      up(a[4], l[2] + r[4] + de);
    
      up(a[2], l[3] + r[0] + se);
      up(a[0], l[3] + r[0] + de);
      up(a[4], l[3] + r[1] + se);
      up(a[1], l[3] + r[1] + de);
      up(a[2], l[3] + r[2] + de);
      up(a[4], l[3] + r[3] + se);
      up(a[3], l[3] + r[3] + de);
      up(a[4], l[3] + r[4] + de);
    
      up(a[2], l[4] + r[0] + de);
      up(a[4], l[4] + r[1] + de);
      up(a[4], l[4] + r[3] + de);
    }
    void build(int k, int l, int r) {
      t[k].l = l, t[k].r = r;
      if (l == r) {
        t[k].a[0] = d[l];
        return;
      }
      int mid = (l + r) >> 1;
      build(k << 1, l, mid);
      build(k << 1 | 1, mid + 1, r);
      int se = std::min(::r[mid][0], ::r[mid][1]), de = ::r[mid][0] + ::r[mid][1];
      update(t[k].a, t[k << 1].a, t[k << 1 | 1].a, se, de);
    }
    void query(int k, int x, int y, int res[5]) {
      int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
      if (x <= l && r <= y) {
        memcpy(res, t[k].a, sizeof(t[k].a));
        return;
      }
      int se = std::min(::r[mid][0], ::r[mid][1]), de = ::r[mid][0] + ::r[mid][1];
      int r1[5], r2[5];
      if (x <= mid) {
        if (y > mid) {
          query(k << 1, x, y, r1);
          query(k << 1 | 1, x, y, r2);
          update(res, r1, r2, se, de);
        } else
          query(k << 1, x, y, res);
      } else
        query(k << 1 | 1, x, y, res);
    }
    void change(int k, int x, int y) {
      int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
      if (x <= l && r <= y) {
        if (l == r) {
          t[k].a[0] = d[l];
          return;
        } else {
          int se = std::min(::r[mid][0], ::r[mid][1]),
              de = ::r[mid][0] + ::r[mid][1];
          update(t[k].a, t[k << 1].a, t[k << 1 | 1].a, se, de);
        }
      } else {
        int se = std::min(::r[mid][0], ::r[mid][1]), de = ::r[mid][0] + ::r[mid][1];
        if (x <= mid)
          change(k << 1, x, y);
        if (y > mid)
          change(k << 1 | 1, x, y);
        update(t[k].a, t[k << 1].a, t[k << 1 | 1].a, se, de);
      }
    }
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("input", "r", stdin);
    #endif
      scanf("%d %d", &N, &M);
      for (int i = 1; i < N; i++) {
        int x;
        scanf("%d", &x);
        r[i][0] = l[i + 1][0] = x;
      }
      for (int i = 1; i < N; i++) {
        int x;
        scanf("%d", &x);
        r[i][1] = l[i + 1][1] = x;
      }
      for (int i = 1; i <= N; i++) {
        int x;
        scanf("%d", &x);
        u[i] = d[i] = x;
      }
      build(1, 1, N);
      while (M--) {
        char ch[5];
        scanf("%s", ch);
        if (ch[0] == 'Q') {
          int A[5], x, y;
          scanf("%d %d", &x, &y);
          query(1, x, y, A);
          printf("%d
    ", A[0]);
        } else {
          int x0, y0, x1, y1, v;
          scanf("%d %d %d %d %d", &x0, &y0, &x1, &y1, &v);
          x0--, x1--;
          if (x0 == x1) {
            if (y0 > y1)
              std::swap(y0, y1);
            r[y0][x0] = l[y1][x0] = v;
            change(1, y0, y1);
          } else {
            u[y0] = d[y0] = v;
            change(1, y0, y1);
          }
        }
      }
    }
    
    
  • 相关阅读:
    用WinForm写的员工考勤项目!!!!!!
    洛谷P1892《[BOI2003]团伙》
    洛谷P1821《[USACO07FEB]银牛派对Silver Cow Party》
    洛谷P1149《火柴棒等式》
    2017 国庆清北刷题冲刺班《角谷猜想》
    洛谷P2330《[SCOI2005]繁忙的都市》
    洛谷P1955《[NOI2015]程序自动分析》
    洛谷P1536《村村通》
    Windows 10 体验记
    洛谷P1102《A-B数对》
  • 原文地址:https://www.cnblogs.com/gengchen/p/6599080.html
Copyright © 2020-2023  润新知