CodeChef October Lunchtime 2019 Division 2

HIT: Khaled in HIT

题目描述

Khaled 教练是 HIT(Hag Institute of Technology)一位名师。但是,他有一些困扰。

最近,Khaled 教练正在教一门课,讲使用电视天线构建 8G 网络并使用汇编语言对它们进行编程。他的班级有 \(N\) 名学生(编号从 \(1\)\(N\));由于某种原因,该数字总是 \(4\) 的倍数。期末考试已结束,Khaled 拿到了他所有 \(N\) 名学生的分数。对于每个 \(i\),第 \(i\) 个学生的分数为 \(A_i\);每个分数是 \(0\)\(100\) 之间的整数。当前,分数等级分布如下:

  • 分数小于 \(60\) 为 D 级
  • 分数大于或等于 \(60\),但小于 \(75\) 则为 C 级
  • 分数大于或等于 \(75\) 但小于 \(90\) 的 B 级
  • 分数等于或大于 \(90\) 的 A 级

但是,Khaled 教练对此并不满意。他希望得到每个等级(A,B,C 和 D)的恰好有 \(\frac{N}{4}\) 名学生,以便各等级成绩达到完美平衡。学生的分数无法更改,但等级之间的界限可以更改。因此,他想选择三个整数 \(x, y, z\),并将等级分布规定为以下形式(可以发现原来的方案中,\(x = 60, y = 75, z = 90\)):

  • 分数小于 \(x\) 为 D 级
  • 分数大于或等于 \(x\),但小于 \(y\) 则为 C 级
  • 分数大于或等于 \(y\),但小于 \(z\) 的 B 级
  • 分数等于或大于 \(z\) 的 A 级

请你找到找到能使等级达到完美平衡的界限 \(x\), \(y\), \(z\)。如果有多种方案,请选择 \(x + y + z\) 最大的方案(因为教练 Khaled 希望显得比他的学生更聪明);可以证明,最多只有一种这样的方案。有时,不存在满足条件选择界限的方法,此时 Khaled 教练会引咎辞职,因为他的试题质量低下。

输入格式

输入数据第一行包含一个整数 \(T\),表示数据组数。接下来是 \(T\) 组数据。
每组数据第一行包含一个整数 \(N\)
第二行包含 \(N\) 个整数 \(A_1, A_2, \cdots, A_N\)

输出格式

对于每组数据,如果无解,输出一行包含一个整数 \(-1\);否则输出一行包含三个整数 \(x, y, z\)

数据范围

  • \(1 \le T \le 1000\)
  • \(4 \le N \le 100\)
  • \(N\) 能被 \(4\) 整除
  • \(0 \le A_i \le 100\)
  • 各组数据中 \(N\) 的和不超过 \(5000\)

子数据集

  • 数据集 1(100 分):无特殊限制

样例数据

输入
6
4
90 25 60 75
8
27 29 92 92 67 67 85 92
4
0 1 2 3
4
100 100 100 100
4
30 30 40 50
4
30 40 40 50
输出
60 75 90
-1
1 2 3
-1
-1
-1

样例解释

第一组数据中,默认方案就是最好的方案。
第四组数据中,所有学生分数都一样,因此不可能有满足条件的方案。

时限

\(1\)

在四分点处截断即可。

时间复杂度 \(\mathcal{O}(n \log n)\)

#include <algorithm>
#include <cstdio>
#include <cstring>

const int MaxN = 100 + 5;

int T, N;
int A[MaxN];

void init() {
  scanf("%d", &N);
  for (int i = 1; i <= N; ++i) scanf("%d", &A[i]);
}

void solve() {
  std::sort(A + 1, A + 1 + N);
  for (int i = 1; i + (N / 4) <= N; i += (N / 4)) {
    if (A[i + (N / 4) - 1] == A[i + (N / 4)]) {
      puts("-1");
      return;
    }
  }
  for (int i = 1; i + (N / 4) <= N; i += (N / 4))
    printf("%d%c", A[i + (N / 4)], " \n"[i + (N / 2) > N]);
}

int main() {
  scanf("%d", &T);
  for (int t = 1; t <= T; ++t) {
    init();
    solve();
  }
  return 0;
}

INVYCNT: Counting Inversions Revisited

题目描述

Almir 有一个短序列 \(A_1, A_2, \dots, A_N\)。他决定把这个序列复制 \(K\) 次并依次首尾相接起来,得到序列 \(X_1, X_2, \dots, X_{NK}\),其中对于每个 \(1 \le i \le N\)\(0 \le j < K\),有 \(X_{j \cdot N + i} = A_i\)

例如,当 \(A = (1, 2, 3)\)\(K = 4\),得到的序列为 \(X = (1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3)\)

对于满足 \(1 \le i < j \le N\) 的数对 \((i, j)\),当 \(X_i > X_j\) 时我们称 \((i, j)\)\(X\) 的一个逆序对。

请你算出最后得到的序列 \(X\) 中的逆序对的数量。

输入格式

输入数据第一行包含一个整数 \(T\),表示数据组数。接下来是 \(T\) 组数据。
每组数据第一行包含两个整数 \(N, K\)
第二行包含 \(N\) 个整数 \(A_1, A_2, \dots, A_N\)

输出格式

对于每组数据,输出一行包含一个整数表示 \(X\) 中的逆序对的数量。

数据范围

  • \(1 \le T \le 1000\)
  • \(1 \le N \le 100\)
  • \(1 \le K \le 10^6\)
  • \(1 \le A_i \le 10^9\)

子数据集

  • 数据集 1(100 分):无特殊限制

样例数据

输入
2
3 3
2 1 3
4 100
99 2 1000 24
输出
12
30000

时限

\(1\)

把答案分为两类统计:段内的和跨段的。

段内的就是 \(A\) 的逆序对个数乘上出现次数 \(K\)

跨段的就是对每个数,找出 \(A\) 中比它小的数个数,然后乘上出现次数,即一个等差数列 \((K - 1) + (K - 2) + \cdots + 1\)

时间复杂度 \(\mathcal{O}(n \log n)\)

#include <algorithm>
#include <cstdio>
#include <cstring>

const int MaxN = 100 + 5;

int Te, N, K;
int A[MaxN], D[MaxN];
int Buc[MaxN];

struct BIT {
  int t[MaxN];

  inline int lowbit(int i) { return i & -i; }

  inline void clear() {
    memset(t, 0, sizeof t);
  }

  inline void update(int x, int v) {
    if (x == 0) {
      t[0] += v;
      return;
    }
    for (int i = x; i <= N; i += lowbit(i))
      t[i] += v;
  }

  inline int query(int x) {
    int res = t[0];
    for (int i = x; i > 0; i -= lowbit(i))
      res += t[i];
    return res;
  }
};
BIT T;

void init() {
  T.clear();
  memset(Buc, 0, sizeof Buc);
  scanf("%d %d", &N, &K);
  for (int i = 1; i <= N; ++i) {
    scanf("%d", &A[i]);
    D[i] = A[i];
  }
  std::sort(D + 1, D + 1 + N);
  for (int i = 1; i <= N; ++i)
    A[i] = std::lower_bound(D + 1, D + 1 + N, A[i]) - D;
}

void solve() {
  long long ans1 = 0;
  for (int i = N; i >= 1; --i) {
    ans1 += T.query(A[i] - 1);
    T.update(A[i], 1);
  }
  ans1 *= K;
  long long ans2 = 0;
  for (int i = 1; i <= N; ++i) Buc[A[i]]++;
  for (int i = 1; i <= N; ++i) Buc[i] += Buc[i - 1];
  for (int i = 1; i <= N; ++i)
    ans2 += 1LL * Buc[A[i] - 1] * K * (K - 1) / 2;
  printf("%lld\n", ans1 + ans2);
}

int main() {
  scanf("%d", &Te);
  for (int t = 1; t <= Te; ++t) {
    init();
    solve();
  }
  return 0;
}

BOXGAM97: Box Game

题目描述

桌子上有 \(N\) 个盒子,从左到右从 \(1\)\(N\) 编号。每个盒子上都写有一个数字;我们用 \(A_i\) 来表示写在 \(i\) 号盒子上的数字。

Jafar 和 Almir 这两个玩家在玩一个持续 \(K\) 回合的游戏。在游戏开始之前,他们掷硬币决定谁先行动。你将根据掷硬币的结果被告知起始玩家。

  • 在第一个回合中,先手玩家将一个球放置在他选择的任意盒子中。
  • 之后,两名玩家进行游戏。(因此,在第二个回合中,另一名玩家将行动)。
  • 在每一回合,行动的玩家可以将球向左移动一个盒子(除非已在 \(1\) 号盒子中)或向右移动一个盒子(除非已在 \(N\) 号盒子中)。

Jafar 的目标是,在比赛结束时,装有球的盒子上写的数字尽可能大。相反,Almir 希望该数字尽可能小。

你知道哪个玩家先手。假设两个玩家都按最优策略行动,试确定游戏的结果。

输入格式

输入数据第一行包含一个整数 \(T\),表示数据组数。接下来是 \(T\) 组数据。
每组数据第一行包含三个整数 \(N, K, P\),其中 \(P\) 表示先手玩家(当 \(P = 0\) 为 Jafer,当 \(P = 1\) 为 Almir)。
第二行包含 \(N\) 个整数 \(A_1, A_2, \dots, A_N\)

输出格式

对于每组数据,输出一行包含在双方都按最优策略行动的前提下的游戏结果。

数据范围

  • \(1 \le T \le 1000\)
  • \(2 \le N \le 100\)
  • \(1 \le K \le 10^9\)
  • \(0 \le P \le 1\)
  • \(1 \le Ai \le 10^9\)

子数据集

  • 数据集 1(100 分):无特殊限制

样例数据

输入
2
4 3 0
4 1 9 5
4 3 1
4 1 9 5
输出
9
1

时限

\(1\)

由于 \(P = 1\) 可以通过把所有 \(A_i\) 转为相反数的方式转为 \(P = 0\),因此以下只讨论 \(P = 0\) 的情况。

\(K\) 的奇偶性讨论。

\(K\) 为奇数时,无论后手怎么操作,先手都能把球移回来原先的位置。因此先手 Jafer 只需要把球放在最大的盒子上即可。

\(K\) 为偶数时,可以证明每次先手所做的操作如果没有抵消后手所做的操作(比如说后手往左操作,先手也往左操作),是不会让结果更优的,因为这样相当于换了一个出发点。因此只剩下后手最后一次机会进行移动,而后手一定移动到较小的位置。那么我们设 \(B_i = \min(A_{i - 1}, A_{i + 1})\)\(B_i\) 的最大值就是答案。

时间复杂度 \(\mathcal{O}(n)\)

#include <algorithm>
#include <cstdio>
#include <cstring>

const int MaxN = 100 + 5;
const int INF = 0x7F7F7F7F;

int Te, N, K, P;
int A[MaxN];

void init() {
  scanf("%d %d %d", &N, &K, &P);
  for (int i = 1; i <= N; ++i) scanf("%d", &A[i]);
}

inline void print(int x) {
  if (P == 0) printf("%d\n", x);
  else printf("%d\n", -x);
}

void solve() {
  if (P == 1)
    for (int i = 1; i <= N; ++i) A[i] *= -1;
  if (K % 2 == 0) {
    int Mx = -INF;
    A[0] = A[N + 1] = INF + 1;
    for (int i = 1; i <= N; ++i) {
      int res = std::min(A[i - 1], A[i + 1]);
      Mx = std::max(Mx, res);
    }
    print(Mx);
  } else {
    int Mx = -INF;
    for (int i = 1; i <= N; ++i)
      Mx = std::max(Mx, A[i]);
    print(Mx);
  }
}

int main() {
  scanf("%d", &Te);
  for (int t = 1; t <= Te; ++t) {
    init();
    solve();
  }
  return 0;
}

INVZCNT: Counting Inversions Again!

题目描述

给定一个非负整数序列 \(A_1, A_2, \dots, A_N\)。对于满足 \(1 \le x < y \le N\) 的数对 \((x, y)\),当 \(A_x > A_y\) 时我们称 \((x, y)\) 是一个逆序对。

你需要回答 \(Q\) 次询问(标号 \(1\)\(Q\))。在第 \(i\) 次询问中:

  • 给定一个非负整数 \(K_i\)
  • 考虑序列 \(B_1, B_2, \dots, B_N\),其中 \(B_j = A_j \oplus K_i\)
  • 你需要找到 \(B\) 中的逆序对的数量

输入格式

输入数据第一行包含一个整数 \(T\),表示数据组数。接下来是 \(T\) 组数据。
每组数据第一行包含两个整数 \(N, Q\)
第二行包含 \(N\) 个整数 \(A_1, A_2, \dots, A_N\)
第三行包含 \(Q\) 个整数 \(K_1, K_2, \dots, K_Q\)

输出格式

对于每组数据,输出 \(Q\) 行,每行包含一个整数表示该询问的答案(逆序对数)。

数据范围

  • \(1 \le T \le 10\)
  • \(1 \le N, Q \le 10^6\)
  • \(0 \le K_i < 2^{31}\)
  • \(0 \le A_i < 2^{31}\)
  • 各组数据中 \(N\) 的总和不超过 \(10^6\)
  • 各组数据中 \(Q\) 的总和不超过 \(10^6\)

子数据集

  • 数据集 1(50 分):
    • \(1 \le Q ≤ 5\)
    • \(1 \le N \le 10^5\)
    • 各组数据中 \(N\) 的总和不超过 \(10^5\)
    • 各组数据中 \(Q\) 的总和不超过 \(5\)
  • 数据集 2(50 分):无特殊限制

样例数据

输入
1
4 4
3 1 2 0
0
1
2
3
输出
5
3
3
1

时限

\(3\)

首先求出初始逆序对数 \(res\),考虑每次异或对答案的影响。

发现对于两个位置 \(i < j\),倘若 \(A_i > A_j\),那么一定存在一位 \(d\) \((0 \le d \le 30)\),满足 \(A_i, A_j\) 在二进制表示中,第 \(30\) 位到第 \(d + 1\) 位都相同,第 \(d\) 位上 \(A_i\)\(0\)\(A_j\)\(1\)。那么一旦异或的 \(K\) 的第 \(d\) 位为 \(1\),就一定会改变这一对的大小关系,即减小一个逆序对。反之 \(A_i < A_j\) 亦然,可能增加一个逆序对。

而这是改变逆序对的必然条件:任意两个数只会对它们那个不同的最高位产生影响。

因此我需要对于每一位求出,若 \(K\) 的这一位有 \(1\),对答案的贡献量是多少。这只需要从左到右扫一遍,存 \(31\) 个哈希表表示考虑了第 \([30, \cdots, i]\) 位,值为 \(x\) (也就是小于 \(i\) 的位全部当 \(0\) 处理)的方案数,那么每次枚举到一个新数,用 \(x \oplus 2^i\) 的方案数相应地改变这位上的贡献了,然后把 \(x\) 加进哈希表里。

查询时直接每一位的改变量相加即可。

时间复杂度 \(\mathcal{O}(n \log A_i)\)

#include <algorithm>
#include <cstdio>
#include <cstring>

const int MaxN = 1000000 + 5;
const int Mod = 233333;

int Te, N, Q;
int A[MaxN];
long long F[31];

struct Hash {
  int cnt;
  int Head[Mod], Next[MaxN], V1[MaxN], F[MaxN];
 
  inline void clear() {
    memset(Head, 0, sizeof Head);
    for (int i = 1; i <= cnt; ++i) Next[i] = V1[i] = F[i] = 0;
    cnt = 0;
  }
 
  inline void add(int x) {
    int h = x % Mod;
    for (int i = Head[h]; i; i = Next[i]) {
      if (V1[i] == x) {
        F[i]++;
        return;
      }
    }
    cnt++;
    V1[cnt] = x; F[cnt] = 1;
    Next[cnt] = Head[h]; Head[h] = cnt;
  }

  inline int query(int x) {
    int h = x % Mod;
    for (int i = Head[h]; i; i = Next[i])
      if (V1[i] == x) return F[i];
    return 0;
  }
};
Hash H;

void init() {
  for (int i = 0; i <= 30; ++i) F[i] = 0;
  scanf("%d %d", &N, &Q);
  for (int i = 1; i <= N; ++i) scanf("%d", &A[i]);
}

namespace CalcInv {
  struct BIT {
    int t[MaxN];
    inline void clear() { for (int i = 0; i <= N; ++i) t[i] = 0; }
    inline int lowbit(int i) { return i & -i; }
    inline void update(int x, int v) {
      if (x == 0) { t[0] += v; return; }
      for (int i = x; i <= N; i += lowbit(i)) t[i] += v;
    }
    inline int query(int x) {
      int res = t[0];
      for (int i = x; i > 0; i -= lowbit(i)) res += t[i];
      return res;
    }
  };

  int a[MaxN], d[MaxN];
  BIT T;

  long long solve() {
    T.clear();
    for (int i = 1; i <= N; ++i) d[i] = A[i];
    std::sort(d + 1, d + 1 + N);
    for (int i = 1; i <= N; ++i) a[i] = std::lower_bound(d + 1, d + 1 + N, A[i]) - d;
    long long res = 0;
    for (int i = N; i >= 1; --i) {
      res += T.query(a[i] - 1);
      T.update(a[i], 1);
    }
    return res;
  }
}

void solve() {
  long long res = CalcInv::solve();
  for (int j = 0; j <= 30; ++j) {
    H.clear();
    for (int i = 1; i <= N; ++i) {
      int x = (A[i] >> j);
      if (x & 1) {
        F[j] += H.query(x ^ 1);
        H.add(x);
      } else {
        F[j] -= H.query(x | 1);
        H.add(x);
      }
    }
  }
  for (int i = 1; i <= Q; ++i) {
    int x;
    long long ans = res;
    scanf("%d", &x);
    for (int j = 0; j <= 30; ++j)
      if (x & (1 << j)) ans += F[j];
    printf("%lld\n", ans);
  }
}

int main() {
  scanf("%d", &Te);
  for (int t = 1; t <= Te; ++t) {
    init();
    solve();
  }
  return 0;
}

DDQUERY: Double Distance Query

题目描述

给定一棵含 \(N\) 个点(标号 \(1\)\(N\))的无权树。我们记点 \(p\) 和点 \(q\) 之间的距离为 \(d(p, q)\)

你需要回答 \(Q\) 次询问。每次询问中,给定参数 \(a, d_a, b, d_b\),需要找到一个点 \(x\) 满足 \(d(x, a) = d_a\)\(d(x, b) = d_b\),或者判断这样的点不存在。

输入格式

输入数据第一行包含一个整数 \(T\),表示数据组数。接下来是 \(T\) 组数据。
每组数据第一行包含两个整数 \(N, Q\)
接下来的 \(N - 1\) 行每行包含两个整数 \(u, v\),表示点 \(u\) 和点 \(v\) 之间有一条边相连。
接下来的 \(Q\) 行每行包含四个整数 \(a, d_a, b, d_b\),描述一个询问。

输出格式

对于每次询问输出一行包含一个整数表示一个满足该询问的点的标号,或者输出 \(-1\) 表示无解。
当询问有多解时,输出任意一个即可。

数据范围

  • \(1 \le T \le 1000\)
  • \(1 \le N, Q \le 10^6\)
  • \(1 \le u, v \le N\)
  • 给定的图是一棵树
  • \(1 \le a, b \le N\)
  • \(1 \le d_a, d_b < N\)
  • 各组数据中 \(N\) 的和不超过 \(10^6\)
  • 各组数据中 \(Q\) 的和不超过 \(10^6\)

子数据集

  • 数据集 1(50 分):
    • \(1 \le N, Q \le 1000\)
    • 各组数据中 \(N\) 的和不超过 \(1000\)
    • 各组数据中 \(Q\) 的和不超过 \(1000\)
  • 数据集 2(50 分):无特殊限制

样例数据

输入
1
5 3
1 2
2 3
3 4
3 5
2 1 4 1
2 2 4 2
1 1 2 1
输出
3
5
-1

时限

\(6\)

求解某个距离 \(u\)\(d\) 的点太难了,我们不妨求 \(u\) 能到达的最远点 \(k\),然后用 \(u \to k\) 上的第 \(d\) 个点作为答案。

路径分为三类:\(x\)\(a\) 到达 \(b\)\(x\)\(b\) 到达 \(a\)\(x\)\(a \leftrightarrow b\) 这条路径上插入,然后分头走到 \(a\)\(b\)

注意一下找最远点的过程中,不能与 \(a \leftrightarrow b\) 这条路径重合。因此需要对每个点维护三个值:最深子树中最深的节点、次深子树中最深的节点、第三深子树中最深的节点,然后判断是否重合来决定取哪个作为最远点。

向上的最远点可以边深搜边 DP 出来。

分类讨论处理一下就可以了,时间复杂度 \(\mathcal{O}(n \log n)\)

#include <algorithm>
#include <cstdio>
#include <cstring>

const int MaxN = 1000000 + 5;
const int MaxLog = 20;

struct Graph {
  int cnte;
  int Head[MaxN], To[MaxN * 2], Next[MaxN * 2];

  inline void addEdge(int from, int to) {
    cnte++; To[cnte] = to;
    Next[cnte] = Head[from]; Head[from] = cnte;
  }
};

int Te, N, Q;
int Fa[MaxLog + 1][MaxN], Dep[MaxN];
int Id[MaxN], Siz[MaxN], Dfc;
int D[MaxN];
Graph G;

void init() {
  scanf("%d %d", &N, &Q);
  for (int i = 1; i < N; ++i) {
    int u, v;
    scanf("%d %d", &u, &v);
    G.addEdge(u, v);
    G.addEdge(v, u);
  }
}

struct Triple {
  int st, nd, rd;

  Triple(int _st = 0, int _nd = 0, int _rd = 0) {
    st = _st;
    nd = _nd;
    rd = _rd;
  }

  inline void add(int x) {
    if (Dep[x] > Dep[st]) {
      rd = nd;
      nd = st;
      st = x;
    } else if (Dep[x] > Dep[nd]) {
      rd = nd;
      nd = x;
    } else if (Dep[x] > Dep[rd]) {
      rd = x;
    }
  }
};
Triple A[MaxN];

void dfs1(int u) {
  Dfc++;
  Id[u] = Dfc;
  Siz[u] = 1;
  A[u] = Triple(u, u, u);
  for (int i = G.Head[u]; i; i = G.Next[i]) {
    int v = G.To[i];
    if (v == Fa[0][u]) continue;
    Fa[0][v] = u;
    Dep[v] = Dep[u] + 1;
    for (int j = 1; (1 << j) <= Dep[v]; ++j)
      Fa[j][v] = Fa[j - 1][Fa[j - 1][v]];
    dfs1(v);
    Siz[u] += Siz[v];
    A[u].add(A[v].st);
  }
}

inline int getKthParent(int u, int k) {
  for (int i = MaxLog; i >= 0; --i)
    if (k & (1 << i)) u = Fa[i][u];
  return u;
}

inline int getLca(int u, int v) {
  if (Dep[u] < Dep[v]) std::swap(u, v);
  u = getKthParent(u, Dep[u] - Dep[v]);
  if (u == v) return u;
  for (int i = MaxLog; i >= 0; --i)
    if (Fa[i][u] != Fa[i][v]) {
      u = Fa[i][u];
      v = Fa[i][v];
    }
  return Fa[0][u];
}

inline int pathLen(int u, int v) {
  int l = getLca(u, v);
  return Dep[u] + Dep[v] - 2 * Dep[l];
}

inline int pathKth(int u, int v, int k) {
  int l = getLca(u, v);
  int d = Dep[u] + Dep[v] - 2 * Dep[l];
  if (k > d) return -1;
  if (k <= Dep[u] - Dep[l]) return getKthParent(u, k);
  else return getKthParent(v, d - k);
}

inline bool inSubtree(int u, int v) {
  if (Id[u] <= Id[v] && Id[v] < Id[u] + Siz[u]) return true;
  else return false;
}

inline bool isParent(int u, int v) {
  int d = Dep[u] - Dep[v];
  if (d > 0 && getKthParent(u, d) == v) return true;
  else return false;
}

inline int getSon(int u, int f) {
  int d = Dep[u] - Dep[f];
  return getKthParent(u, d - 1);
}

void dfs2(int u, int res) {
  D[u] = res;
  for (int i = G.Head[u]; i; i = G.Next[i]) {
    int v = G.To[i];
    if (v == Fa[0][u]) continue;
    int newRes = inSubtree(v, A[u].st) ? A[u].nd : A[u].st;
    if (pathLen(v, newRes) < pathLen(v, res)) newRes = res;
    dfs2(v, newRes);
  }
}

void solve() {
  dfs1(1);
  dfs2(1, 1);
  for (int q = 1; q <= Q; ++q) {
    int u, du, v, dv;
    scanf("%d %d %d %d", &u, &du, &v, &dv);
    if (Dep[u] < Dep[v]) {
      std::swap(u, v);
      std::swap(du, dv);
    }
    int l = getLca(u, v);
    int d = Dep[u] + Dep[v] - 2 * Dep[l];

    if (du + d == dv) {
      int x = A[u].st;
      if (Dep[x] - Dep[u] >= du) {
        printf("%d\n", pathKth(u, x, du));
        continue;
      }
    }
    if (dv + d == du) {
      int x = A[v].st;
      if (inSubtree(getSon(u, l), x) == true) x = A[v].nd;
      if (Dep[x] - Dep[v] >= dv) {
        printf("%d\n", pathKth(v, x, dv));
        continue;
      }
      if (l == v) {
        x = D[v];
        if (pathLen(x, v) >= dv) {
          printf("%d\n", pathKth(v, x, dv));
          continue;
        }
      }
    }
    if (du + dv >= d && (du + dv - d) % 2 == 0) {
      int k = (du + dv - d) / 2;
      int m = pathKth(u, v, du - k);
      int x = A[m].st;
      if ((isParent(u, m) && inSubtree(getSon(u, m), x)) || (isParent(v, m) && inSubtree(getSon(v, m), x))) x = A[m].nd;
      if ((isParent(u, m) && inSubtree(getSon(u, m), x)) || (isParent(v, m) && inSubtree(getSon(v, m), x))) x = A[m].rd;
      if (Dep[x] - Dep[m] >= k) {
        printf("%d\n", pathKth(m, x, k));
        continue;
      }
      if (m == l) {
        x = D[m];
        if (pathLen(x, m) >= k) {
          printf("%d\n", pathKth(m, x, k));
          continue;
        }
      }
    }

    puts("-1");
  }
}

void clear() {
  for (int i = 1; i <= N; ++i) {
    Dep[i] = 0;
    Siz[i] = 0;
    Id[i] = 0;
    D[i] = 0;
    G.Head[i] = 0;
    for (int j = 0; j <= MaxLog; ++j) Fa[j][i] = 0;
    A[i] = Triple();
  }
  for (int i = 1; i <= G.cnte; ++i)
    G.To[i] = G.Next[i] = 0;
  G.cnte = 0;
  Dfc = 0;
}

int main() {
  scanf("%d", &Te);
  for (int t = 1; t <= Te; ++t) {
    init();
    solve();
    clear();
  }
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/tweetuzki/p/11749781.html