bzoj 5329 [SDOI2018] 战略游戏

bzoj 5329 [SDOI2018] 战略游戏

Solution

很容易想到虚树

然后发现是一个图。。。

现学圆方树,套上去,做完了(模板题?)

就是直接上广义圆方树先把这玩意转换成一棵树,然后对当前询问建立虚树,断掉虚树里任何一个点都合法(包括不出现的点,指那些在某个点和其虚树上父亲之间的点),统计一下即可

Code

// Copyright lzt
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef std::pair<int, int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<long long, long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i, j, k)  for (register int i = (int)(j); i <= (int)(k); i++)
#define rrep(i, j, k) for (register int i = (int)(j); i >= (int)(k); i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__)
 
inline ll read() {
  ll x = 0, f = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch <= '9' && ch >= '0') {
    x = 10 * x + ch - '0';
    ch = getchar();
  }
  return x * f;
}
 
const int maxn = 200200;
int tc, n, m, Q, tot, tim;
int dfn[maxn], low[maxn], S[maxn], s[maxn], q[maxn];
int fa[maxn], dep[maxn], dis[maxn], sz[maxn], son[maxn], top[maxn];
 
struct Graph {
  int head[maxn], to[maxn << 1], nxt[maxn << 1], tot;
  void init() {
    memset(head, 0, sizeof(head));
    tot = 0;
  }
  void addedge(int x, int y) {
    to[++tot] = y; nxt[tot] = head[x]; head[x] = tot;
    to[++tot] = x; nxt[tot] = head[y]; head[y] = tot;
  }
} G1, G2;
 
void Tarjan(int u) {
  tim++; dfn[u] = low[u] = tim; S[++S[0]] = u;
  for (int i = G1.head[u]; i; i = G1.nxt[i]) {
    int v = G1.to[i];
    if (!dfn[v]) {
      Tarjan(v); low[u] = min(low[u], low[v]);
      if (low[v] >= dfn[u]) { // 单独一条边连接两个点也得算作点双
        G2.addedge(++tot, u); int x = 0;
        do {
          x = S[S[0]]; S[0]--;
          G2.addedge(tot, x);
        } while (x != v);
      }
    }
    else low[u] = min(low[u], dfn[v]);
  }
}
 
void dfs1(int u, int pa) {
  fa[u] = pa; dep[u] = dep[pa] + 1; dis[u] = dis[pa] + (u <= n); sz[u] = 1;
  for (int i = G2.head[u]; i; i = G2.nxt[i]) {
    int v = G2.to[i];
    if (v == pa) continue;
    dfs1(v, u); sz[u] += sz[v];
    if (sz[v] > sz[son[u]]) son[u] = v;
  }
}
 
void dfs2(int u, int tp) {
  top[u] = tp; dfn[u] = ++tim;
  if (son[u]) dfs2(son[u], tp);
  for (int i = G2.head[u]; i; i = G2.nxt[i]) {
    int v = G2.to[i];
    if (v == fa[u] || v == son[u]) continue;
    dfs2(v, v);
  }
  low[u] = tim;
}
 
int lca(int x, int y) {
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) swap(x, y);
    x = fa[top[x]];
  }
  return dep[x] < dep[y] ? x : y;
}
 
bool cmp(int x, int y) {
  return dfn[x] < dfn[y];
}
 
void work() {
  tc = read();
  while (tc--) {
    n = read(), m = read(); G1.init(); G2.init(); tim = 0; tot = n;
    rep(i, 1, m) {
      int x = read(), y = read();
      G1.addedge(x, y);
    }
    memset(dfn, 0, sizeof(dfn));
    memset(son, 0, sizeof(son));
    rep(i, 1, n) if (!dfn[i]) Tarjan(i);
    tim = 0; dfs1(1, 0); dfs2(1, 1);
    Q = read();
    while (Q--) {
      int k = read(), len = k, tp = 0, ans = 0;
      rep(i, 1, k) s[i] = read();
      sort(s + 1, s + k + 1, cmp);
      rep(i, 1, k - 1) s[++len] = lca(s[i], s[i + 1]);
      sort(s + 1, s + len + 1, cmp);
      len = unique(s + 1, s + len + 1) - (s + 1);
      ans = s[1] <= n;
      rep(i, 1, len) {
        while (tp && low[q[tp]] < dfn[s[i]]) tp--;
        if (tp) ans += dis[s[i]] - dis[q[tp]];
        q[++tp] = s[i];
      }
      printf("%d\n", ans - k);
    }
  }
}
 
int main() {
  #ifdef LZT
  freopen("in", "r", stdin);
  // freopen("out", "w", stdout);
  #endif
 
  work();
 
  #ifdef LZT
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
  #endif
}

Review

第一次写圆方树

如果知道圆方树的话这题感觉很好想

posted @ 2018-12-22 23:24  wawawa8  阅读(170)  评论(0编辑  收藏  举报