Submission #1279587

#TimeUsernameProblemLanguageResultExecution timeMemory
1279587tuanmwillwinvoi26Hexagonal Territory (APIO21_hexagon)C++20
100 / 100
149 ms49560 KiB
#include "hexagon.h"
#include <bits/stdc++.h>

using namespace std;

struct Point {
  int x, y;
  Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
  Point operator+(Point p) const { return Point(x+p.x, y+p.y); }
  Point operator-(Point p) const { return Point(x-p.x, y-p.y); }
  Point operator*(int s) const { return Point(x * s, y * s); }
  bool operator==(Point p) const { return x == p.x && y == p.y; }
  bool operator<(Point p) const { return x == p.x ? y < p.y : x < p.x; }
};

ostream& operator<<(ostream& os, Point p) {
  return os << "(" << p.x << ", " << p.y << ")";
}

constexpr int mod = 1e9 + 7;
const vector<Point> pdirs = { Point(0, 1), Point(1, 1), Point(1, 0),
                              Point(0, -1), Point(-1, -1), Point(-1, 0) };

long long signed_twice_area(vector<Point> & vp) {
  long long ret = 0;
  int n = (int)vp.size() - 1;
  for (int i = 0; i < n; ++i) {
    ret += 1LL * vp[i].x * vp[i+1].y - 1LL * vp[i+1].x * vp[i].y;
  }
  return ret;
}

// assumption: vp circulating the territory in counter-clockwise direction
vector<tuple<Point, Point, int>> find_top_bottom(vector<Point> & vp) {
  vector<tuple<Point, Point, int>> result;
  int n = (int)vp.size()-1;
  for (int i = 0; i < n; ++i) {
    Point a = vp[i], b = vp[(i+1) % n];
    Point bef = vp[(i+n-1) % n], aft = vp[(i+2) % n];
    if (a.x == b.x)
      continue;
    if (a.x < b.x) { // bottom
      int dir;
      if (a.y < b.y) {
        dir = 1;
      } else {
        assert(a.y == b.y);
        dir = 2;
      }
      if (bef.x < a.x || bef.y < a.y)
        a = a + pdirs[dir];
      if (aft.x <= b.x && aft.y < b.y)
        b = b - pdirs[dir];
      if (b < a)
        continue;
      --a.y;
      --b.y;
      result.emplace_back(a, b, -1);
    } else { // top
      int dir;
      if (a.y > b.y) {
        dir = 4;
      } else {
        assert(a.y == b.y);
        dir = 5;
      }
      if (bef.x > a.x || bef.y > a.y)
        a = a + pdirs[dir];
      if (aft.x >= b.x && aft.y > b.y)
        b = b - pdirs[dir];
      if (a < b)
        continue;
      result.emplace_back(b, a, +1);
    }
  }
  return result;
}

struct Column {
  int x, bot, top;
  int minval, mbot, mtop;
  Column(int _x, int _bot, int _top,
         int _minval = -1, int _mbot = -1, int _mtop = -1)
    : x(_x), bot(_bot), top(_top), minval(_minval), mbot(_mbot), mtop(_mtop) {}
  void update(int _minval, int _mbot, int _mtop) {
    minval = _minval;
    mbot = _mbot;
    mtop = _mtop;
    if (mbot > top) {
      minval += mbot - top;
      minval %= mod;
      mbot = mtop = top;
    }
    if (mtop < bot) {
      minval += bot - mtop;
      minval %= mod;
      mbot = mtop = bot;
    }
    mtop = min(mtop, top);
    mbot = max(mbot, bot);
  }
};

constexpr long long inv2 = (mod + 1LL) / 2;
// calc: sum(a + (i-1) * b)) for i = 1..n
long long calc_1(long long a, long long b, long long n) {
  long long ret = (2LL * a + (n-1LL) * b) % mod;
  ret = ret * n % mod;
  ret = ret * inv2 % mod;
  return ret;
}

constexpr long long inv3 = ((mod % 3) == 2 ? (mod + 1LL) : (2LL * mod + 1)) / 3;
// calc: sum(i * (a + (i-1) * b)) for i = 1..n
long long calc_2(long long a, long long b, long long n) {
  long long ret = n * (n + 1LL) % mod;
  ret = ret * inv2 % mod;
  long long mul = (2LL * n + 1LL) * inv3 % mod;
  mul = (a - b + mul * b) % mod;
  ret = ret * mul % mod;
  return ret;
}

long long calc_rectangle(int width, int minval, int same, int down, int up) {
  long long ret = 0;
  long long a = 1LL * minval * same %  mod;
  if (down) {
    a = (a + calc_1(minval+1, 1, down)) % mod;
  }
  ret += calc_1(a, same + down, width);
  if (up) {
    int len = min(up, width);
    ret += calc_2(minval+1, 1, len) + calc_2(minval+1, 1, len-1);
    if (len < width) {
      ret += calc_1(1LL * (minval + len) * up % mod, up, width - len);
    }
    if (len < up) {
      ret += calc_1(1LL * (minval + len + 1) * width % mod, width, up - len);
    }
  }
  return ret % mod;
}

long long calc(int width, int minval, int same, int down, int up,
               bool del_topleft, bool del_botright) {
  long long ret = calc_rectangle(width, minval, same, down, up);
  if (width > 1) {
    int d = width - 1;
    if (del_topleft) {
      assert(up >= d);
      ret -= calc_2(minval + (up - d + 1), 1, d);
    }
    if (del_botright) {
      int len = min(down, d);
      ret -= calc_2(minval + len + down, mod-1, len);
      if (len < d) {
        ret -= calc_2(minval + len+1, 1, d - len);
        long long a = calc_1(minval + len+2, 1, down);
        ret -= calc_1(a, down, d - len);
      }
    }
  }
  ret %= mod;
  if (ret < 0)
    ret += mod;
  return ret;
}

struct Block {
  Column left, right;
  Block(Column _left, Column _right) : left(_left), right(_right) {}

  long long move_right(long long a, long long b) {
    int dx = right.x - left.x;
    long long ret = (left.top - left.bot + right.top - right.bot + 2LL);
    ret = ((ret * (dx + 1) % mod) * inv2 % mod) * a % mod;

    right.update(left.minval + dx, left.mbot, left.mtop + dx);
    int width = right.x - left.x + 1;
    int same = left.mtop - left.mbot + 1;
    int up = right.top - left.mtop;
    int down = left.mbot - left.bot;
    bool del_toplef = left.top < right.top, del_botrig = left.bot < right.bot;
    ret += b * calc(width, left.minval, same, down, up, del_toplef, del_botrig);
    return ret % mod;
  }

  long long move_left(long long a, long long b) {
    int dx = right.x - left.x;
    long long ret = (left.top - left.bot + right.top - right.bot + 2LL);
    ret = ((ret * (dx + 1) % mod) * inv2 % mod) * a % mod;

    left.update(right.minval + dx, right.mbot - dx, right.mtop);
    int width = right.x - left.x + 1;
    int same = right.mtop - right.mbot + 1;
    int down = right.top - right.mtop;
    int up = right.mbot - left.bot;
    bool del_botrig = left.top < right.top, del_toplef = left.bot < right.bot;
    ret += b * calc(width, right.minval, same, down, up, del_toplef,
                    del_botrig);
    return ret % mod;
  }
};

struct Line {
  Point pstart, pdir;
  int id;
  Point get(int x) const {
    return pstart + (pdir * (x - pstart.x));
  }
  bool operator<(const Line & rig) const {
    int x_common = max(pstart.x, rig.pstart.x);
    return get(x_common).y < rig.get(x_common).y;
  }
};

vector<Block> find_blocks(vector<Point> & vp) {
  auto y_events = find_top_bottom(vp);
  vector<int> last(y_events.size()), sign(y_events.size());
  vector<Line> lines(y_events.size());
  vector<pair<int, int>> x_events;
  for (int i = 0; i < (int)y_events.size(); ++i) {
    Point pleft, pright;
    tie(pleft, pright, sign[i]) = y_events[i];
    last[i] = pleft.x;
    lines[i] = {pleft, (pright.y > pleft.y ? Point(1, 1) : Point(1, 0)), i};
    x_events.emplace_back(pleft.x, i);
    x_events.emplace_back(pright.x+1, ~i);
  }
  sort(x_events.begin(), x_events.end());
  set<Line> st;
  vector<Block> result;
  auto check_block = [&](set<Line>::iterator nxt, int x) {
    if (nxt != st.begin() && nxt != st.end() && sign[nxt->id] > 0) {
      auto prv = prev(nxt);
      if (sign[prv->id] < 0 && min(last[prv->id], last[nxt->id]) < x) {
        assert(last[prv->id] == last[nxt->id]);
        int xleft = last[prv->id], xright = x - 1;
        result.emplace_back(
            Column(xleft, prv->get(xleft).y+1, nxt->get(xleft).y),
            Column(xright, prv->get(xright).y+1, nxt->get(xright).y));
        last[prv->id] = last[nxt->id] = x;
      }
    }
  };
  for (auto & e : x_events) {
    int id = e.second;
    if (id < 0) {
      id = ~id;
      auto it = st.upper_bound(lines[id]);
      if (it != st.end())
        check_block(it, e.first);
      assert(it != st.begin());
      --it;
      assert(it->id == id);
      check_block(it, e.first);
      st.erase(it);
    } else {
      auto nxt = st.lower_bound(lines[id]);
      check_block(nxt, e.first);
      st.insert(lines[id]);
    }
  }
  return result;
}

vector<Block> blocks;
vector<vector<pair<int, int>>> tree;

void construct_tree() {
  tree.resize(blocks.size());
  vector<tuple<int, int, int>> events;
  for (int i = 0; i < (int)blocks.size(); ++i) {
    events.emplace_back(blocks[i].left.x, blocks[i].left.bot, i);
    events.emplace_back(blocks[i].right.x+1, blocks[i].right.bot, ~i);
  }
  int idl = -1, idr = -1;
  sort(events.begin(), events.end());
  int cnt = 0;
  for (auto & e : events) {
    int id = get<2>(e);
    if (id < 0)
      idl = ~id;
    else
      idr = id;
    if (idl >= 0 && idr >= 0 && blocks[idl].right.x+1 == blocks[idr].left.x) {
      if (blocks[idl].right.bot <= blocks[idr].left.top &&
          blocks[idr].left.bot <= blocks[idl].right.top) {
        tree[idl].emplace_back(idr, 1);
        tree[idr].emplace_back(idl, -1);
        ++cnt;
      }
    }
  }
  assert(cnt + 1 == (int)blocks.size());
}

int dfs(int v, int par, long long a, long long b) {
  int ret = 0;
  for (auto & e : tree[v]) {
    int u = e.first;
    if (u == par) continue;
    if (e.second > 0) {
      blocks[u].left.update(blocks[v].right.minval+1, blocks[v].right.mbot,
                            blocks[v].right.mtop+1);
      ret = (ret + blocks[u].move_right(a, b)) % mod;
    } else {
      blocks[u].right.update(blocks[v].left.minval+1, blocks[v].left.mbot-1,
                             blocks[v].left.mtop);
      ret = (ret + blocks[u].move_left(a, b)) % mod;
    }
    ret = (ret + dfs(u, v, a, b)) % mod;
  }
  return ret;
}

int draw_territory(int N, int A, int B, vector<int> D, vector<int> L) {
  vector<Point> vp(N+1);
  for (int i = 0; i < N; ++i) {
    vp[i+1] = vp[i] + pdirs[D[i]-1] * L[i];
  }
  if (signed_twice_area(vp) < 0) {
    reverse(vp.begin(), vp.end());
  }
  blocks = find_blocks(vp);
  construct_tree();
  long long ans = 0;
  int root = -1;
  for (int i = 0; i < (int)blocks.size(); ++i) {
    if (blocks[i].left.x > 0) continue;
    if (blocks[i].right.x < 0) continue;
    int top = blocks[i].left.top, bot = blocks[i].left.bot;
    if (blocks[i].left.top < blocks[i].right.top)
      top += -blocks[i].left.x;
    if (blocks[i].left.bot < blocks[i].right.bot)
      bot += -blocks[i].left.x;
    if (bot <= 0 && 0 <= top) {
      assert(root == -1);
      root = i;
      Block left = blocks[root], right = blocks[root];
      left.right = right.left = Column(0, bot, top, 0, 0, 0);
      ans = (ans + left.move_left(A, B)) % mod;
      ans = (ans + right.move_right(A, B)) % mod;
      ans = (ans - calc_1(A, B, top+1) - calc_1(A+B, B, -bot)) % mod;
      blocks[root].left = left.left;
      blocks[root].right = right.right;
    }
  }
  assert(root != -1);
  ans = (ans + dfs(root, -1, A, B)) % mod;
  if (ans < 0)
    ans += mod;
  return ans;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...