[HNOI2016]矿区

题面

给定平面上许多的区域,每次询问一些点所包围的子区域的面积平方之和与总面积之和之比,强制在线。

\(\text{Solution}\)

毒瘤至极

显然我们要维护每一个小区域的面积,由于我们要维护的是一个面,所以我们将平面图转对偶图(不会的戳这里)。面的信息就缩在一个点中,以无穷域为根随便弄出个生成树,对于任意点x处理出以x为根的子树所代表的区域的面积。

那么对于一组询问,我们扫描它的每一条边,看它的反向边是否是它的父亲,如果是,则加上自己子树的面积,否则减去子树的面积。

显然这么毒瘤的题我是码不出的。
不过如果之前学过对偶图,思维难度并不大。

#include <set>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <assert.h>
#include <algorithm>

using namespace std;

#define LL long long
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO debug("GO\n")

inline int rint() {
  register int x = 0, f = 1; register char c;
  while (!isdigit(c = getchar())) if (c == '-') f = -1;
  while (x = (x << 1) + (x << 3) + (c ^ 48), isdigit(c = getchar()));
  return x * f;
}

template<typename T> inline void chkmin(T &a, T b) { a > b ? a = b : 0; }
template<typename T> inline void chkmax(T &a, T b) { a < b ? a = b : 0; }

const int N = 2e5 + 10, M = 1.2e6 + 5;
const double eps = 1e-10;

#define Iter vector<Edge>::iterator

LL ans1, ans2;
int n, m, Q, tot = 1, cnt, root;

struct vec {
  int x, y;
  vec(int _x = 0, int _y = 0) {x = _x, y = _y;}
  vec operator- (const vec &b) const 
  { return vec(x - b.x, y - b.y); }
  LL operator* (const vec &b) const
  { return 1ll * x * b.y - 1ll * y * b.x;}
} P[N];

struct Edge {
  int id, u, v; double angle;//atan2 rad
  bool operator< (const Edge &b) const
  { return fabs(angle - b.angle) < eps ? v < b.v : angle < b.angle; }
} E[M];

int nxt[M], pos[M], fa[M], vis[M], intree[M], ask[M];
LL s[M], ss[M];

vector<Edge> G[N], tree[M];

void add(int u, int v) {
  E[++tot] = (Edge) {tot, u, v, atan2(P[v].y - P[u].y, P[v].x - P[u].x)};
  G[u].push_back(E[tot]);
}

void Build() {
  for (int i = 1; i <= n; ++ i)
    sort(G[i].begin(), G[i].end());
  for (int i = 2; i <= tot; ++ i) {
    int v = E[i].v;
    Iter it = lower_bound(G[v].begin(), G[v].end(), E[i ^ 1]);
    if (it == G[v].begin()) it = G[v].end();
    --it;
    nxt[i] = it->id;
  }
  for (int i = 2; i <= tot; ++ i) {
    if (pos[i]) continue;
    pos[i] = pos[nxt[i]] = ++cnt;
    for (int j = nxt[i]; E[j].v != E[i].u; j = nxt[j], pos[j] = cnt) 
      s[cnt] += (P[E[j].u] - P[E[i].u]) * (P[E[j].v] - P[E[i].u]);
    if (s[cnt] <= 0) root = cnt;
  }
  for (int i = 2; i <= tot; ++ i) 
    tree[pos[i]].push_back((Edge){i, pos[i], pos[i ^ 1]});
}

void DFS(int u, int las) {
  fa[u] = las;
  ss[u] = s[u] * s[u];
  s[u] <<= 1;
  vis[u] = 1;
  for (int i = 0; i < tree[u].size(); ++ i) {
    int v = tree[u][i].v;
    if (vis[v]) continue;
    intree[tree[u][i].id] = intree[tree[u][i].id ^ 1] = 1;
    DFS(v, u);
    s[u] += s[v];
    ss[u] += ss[v];
  }
}

void solve() {
  while (Q --) {
    int tmp = rint(); tmp = (tmp + ans1) % n + 1;
    for (int i = 1; i <= tmp; ++ i) 
      ask[i] = (rint() + ans1) % n + 1;
    ask[tmp + 1] = ask[1];
    ans1 = ans2 = 0;
    for (int i = 1; i <= tmp; ++ i) {
      int x = ask[i], y = ask[i + 1];
      Edge xhc = (Edge) {0, x, y, atan2(P[y].y - P[x].y, P[y].x - P[x].x)};
      Iter it = lower_bound(G[x].begin(), G[x].end(), xhc);
      int id = it->id;
      if (!intree[id]) continue;
      if (fa[pos[id]] == pos[id ^ 1]) 
        ans1 += ss[pos[id]], ans2 += s[pos[id]];
      else 
        ans1 -= ss[pos[id ^ 1]], ans2 -= s[pos[id ^ 1]];
    }
      LL gcd = __gcd(ans1, ans2);
      ans1/=gcd, ans2/=gcd;
      printf("%lld %lld\n", ans1, ans2);
  }
}
int main() {
#ifndef ONLINE_JUDGE
  freopen("xhc.in", "r", stdin);
  freopen("xhc.out", "w", stdout);
#endif
  n = rint(), m = rint(), Q = rint();
  for (int i = 1; i <= n; ++ i) {
    int u = rint(), v = rint();
    P[i] = vec(u, v);
  }
  for (int i = 1; i <= m; ++ i) {
    int x = rint(), y = rint();
    add(x, y), add(y, x);
  }
  Build();
  DFS(root, 0);
  solve();
}
posted @ 2019-03-26 11:57  茶Tea  阅读(109)  评论(0编辑  收藏  举报