JOI Open 2018

hztmax0發表於2024-10-09

T1 Bubble Sort 2

題意:給定一個長度為 \(n\) 的序列 \(a\),進行 \(q\) 次修改,第 \(i\) 次將第 \(x_i\) 個元素的值修改為 \(y_i\)
對於每次操作後,你都需要求出,如果此時對序列進行氣泡排序,需要多少次冒泡才能完成排序。
\(n \le 5 \times 10^5\)

序列有序意味著,每個數前面都沒有大於自身的數。假設一個元素 \(i\) 前面有 \(c_i\) 個數大於自身,考慮進行一次冒泡後 \(c_i\) 個元素中最大的會到 \(i\) 後面,其他的還是在 \(i\) 前面。相當於一次冒泡是 \(\forall c_i > 0, c_i \gets c_i - 1\)。所以答案顯然為

\[\max\limits_{i = 1}^n \bigg{(}\sum\limits_{j = 1}^{i- 1} [a_j > a_i] \bigg{)} \]

對於這個直接樹套樹維護動態二維偏序,時間複雜度 \(O(n \log^2 n)\)。無法透過。

考慮對於一個 \(i\),若存在 \(k > i\) 使得 \(a_k < a_i\),那麼 \(k\) 相對於 \(i\) 不劣,此時我們可以不計算 \(i\) 的貢獻。也就是說,計算答案時只需考慮所有字尾最小值。

那麼我們考慮對限制放縮,對於一個數 \(i\),我們直接欽定 \(i\) 後面的數全部大於 \(i\),此時對於字尾 \(\min\) 的位置答案不變,對於非字尾 \(\min\) 的位置答案變小了,所以這樣做是對的、那麼我們要計算的就是:

\[\max\limits_{i = 1}^n \bigg{(}\sum\limits_{j = 1}^{n} [a_j > a_i] \bigg{)} - (n - i) \]

這個東西考慮用權值線段樹維護,一個數修改時的影響就是一段字首加或減,這個都好維護。

時間複雜度 \(O(n \log n)\)

Code
#include "bubblesort2.h"
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>

#define lc (k << 1)
#define rc ((k << 1) | 1)

using namespace std;

const int N = 5e5 + 5, T = N * 8; 
const int Inf = 1e9;

int n, q, m; 
int a[N], p[N * 2], tx[N], ty[N];
set<int> s[N * 2]; 
int mx[T], tag[T];

void Modify (int k, int x, int y, int L = 1, int R = m) {
  if (L == R) {
    mx[k] = y + tag[k];
    return;
  }
  int mid = (L + R) >> 1;
  if (x <= mid) {
    Modify(lc, x, y, L, mid);
  }
  else {
    Modify(rc, x, y, mid + 1, R);
  }
  mx[k] = max(mx[lc], mx[rc]) + tag[k];
}

void Add (int k, int l, int r, int x, int L = 1, int R = m) {
  if (l <= L && r >= R) {
    tag[k] += x, mx[k] += x;
    return;
  } 
  int mid = (L + R) >> 1;
  if (l <= mid) {
    Add(lc, l, r, x, L, mid);
  }
  if (r > mid) {
    Add(rc, l, r, x, mid + 1, R);
  }
  mx[k] = max(mx[lc], mx[rc]) + tag[k];
}

void Insert_val (int x, int v) {
  s[v].insert(x);
  Modify(1, v, *--s[v].end() - n);
  if (v > 1)
    Add(1, 1, v - 1, 1);
}  

void Remove_val (int x, int v) {
  s[v].erase(x);
  Modify(1, v, s[v].empty() ? -Inf : *--s[v].end() - n);
  if (v > 1) 
    Add(1, 1, v - 1, -1);
}

vector<int> countScans (vector<int> A, vector<int> X, vector<int> V) {
  q = X.size();
  for (auto i : A) {
    a[++n] = i, p[++m] = i; 
  }
  for (auto &i : X) ++i;
  for (auto i : V) {
    p[++m] = i;
  }
  sort(p + 1, p + m + 1);
  m = unique(p + 1, p + m + 1) - p - 1; 
  for (int i = 1; i <= n; ++i) {
    a[i] = lower_bound(p + 1, p + m + 1, a[i]) - p; 
  }
  for (auto &i : V) {
    i = lower_bound(p + 1, p + m + 1, i) - p; 
  }
  fill(mx, mx + m * 4 + 1, -Inf);
  for (int i = 1; i <= n; ++i) {
    Insert_val(i, a[i]);
  } 
  vector<int> ans;
  for (int i = 0; i < q; ++i) {
    int x = X[i], y = V[i];
    Remove_val(x, a[x]);
    Insert_val(x, a[x] = y);
    ans.push_back(mx[1]);
  }
  return ans;
}

T2 Cats or Dogs

花園中有 \(n\) 個小屋,這些小屋透過 \(n - 1\) 條邊相連,構成一棵樹。每個小屋有三種狀態:有一隻貓,有一隻狗,或者什麼都沒有。
定義當前時刻花園的危險級別為,最少要切斷樹上的多少條邊,使得任意一對貓和狗無法互相到達。
進行 \(q\) 次操作,每次操作形如將一個沒有寵物的小屋放一貓或狗,或者將某個小屋內的貓或狗移除,然後求花園的危險級別。
\(1 \le n, q \le 10^5\)

考慮一個平凡的暴力:設 \(f_{i, 0}\) 表示使得 \(i\) 子樹內與 \(i\) 連線的連通塊中沒有貓的最小代價,\(f_{i, 1}\) 類似的表示沒有狗的最小代價。時間複雜度 \(O(nq)\)

然後我們用動態樹分治最佳化。設 \(g_{i, 0}\) 表示 \(i\) 的所有輕子樹內沒有貓的最小代價,\(g_{i, 1}\) 表示 \(i\) 的所有輕子數內沒有狗的最小代價。注意在這裡我們允許花 \(1\) 的代價切斷 \(i \rightarrow son_i\) 的邊。

考慮一個結點 \(u\),我們求出了 \(g_u\) 和其重兒子的 \(f\),則 \(f_u\) 的轉移方程

\[ f_{u, 0} = \min(f_{v, 0}, f_{v, 1} + 1) + g_{u, 0} + [a_u = 1]\infty \\ f_{u ,1} = \min(f_{v, 1}, f_{v, 0} + 1) + g_{u, 1} + [a_u = 2]\infty \]

其中 \(a_u = 1\) 表示 \(u\) 結點處為貓,\(a_u = 2\) 表示 \(u\) 結點處為狗。我們將轉移寫成 \((\min, +)\) 矩陣乘法的形式:

\[ \begin{bmatrix} g_{u, 0} + [a_u = 1]\infty & g_{u, 0} + 1 + [a_u = 1]\infty \\ g_{u, 1} + 1 + [a_u = 2]\infty & g_{u, 1} + [a_u = 2]\infty \end{bmatrix} \ast \begin{bmatrix} f_{v, 0} \\ f_{v, 1} \end{bmatrix} = \begin{bmatrix} f_{u, 0} \\ f_{u, 1} \end{bmatrix} \]

剩下的就是動態樹分治板子了,對於重鏈線段樹維護轉移矩陣,對於輕邊暴力跳即可。

時間複雜度 \(O(n \log^2 n)\)

Code
#include <iostream>
#include <vector>
#include <array>
#include "catdog.h"

using namespace std;
using Vec = array<int, 2>;

const int N = 1e5 + 5; 
const Vec Evec = {0, 0};

int n, a[N];
vector<int> e[N];
int fa[N], siz[N], dep[N], wson[N];
int dfn[N], rmp[N], now, tp[N], bot[N];
int f[N][2], g[N][2];

void Dfs (int u) {
  siz[u] = 1;
  dep[u] = dep[fa[u]] + 1;
  for (auto v : e[u]) {
    if (v != fa[u]) {
      fa[v] = u, Dfs(v);
      siz[u] += siz[v];
      if (siz[v] > siz[wson[u]]) {
        wson[u] = v;
      }
    }
  }
}

void Dfs2 (int u, int t) {
  dfn[u] = ++now;
  rmp[now] = u; 
  tp[u] = t, bot[t] = u; 
  if (wson[u]) {
    Dfs2(wson[u], t);
  }
  for (auto v : e[u]) {
    if (v != fa[u] && v != wson[u]) {
      Dfs2(v, v);
    }
  }
}

struct Mat {
  array<Vec, 2> a;

  Vec& operator[] (int x) {
    return a[x];
  }

  Mat () { a[0][0] = a[0][1] = a[1][0] = a[1][1] = 0; }
  Mat (int p, int q, int r, int s) {
    a[0][0] = p, a[0][1] = q, a[1][0] = r, a[1][1] = s;
  }

  Mat operator* (Mat b) {
    Mat res(N, N, N, N);
    for (int i = 0; i < 2; ++i) {
      for (int j = 0; j < 2; ++j) {
        for (int k = 0; k < 2; ++k) {
          res[i][j] = min(res[i][j], a[i][k] + b[k][j]);
        }
      }
    }
    return res;
  }

  Vec operator* (Vec b) {
    Vec res = {N, N};
    for (int i = 0; i < 2; ++i) {
      for (int j = 0; j < 2; ++j) {
        res[i] = min(res[i], a[i][j] + b[j]);
      }
    }
    return res;
  }
} mat[N];

namespace Seg {
  #define lc (k << 1)
  #define rc ((k << 1) | 1)
  const int T = N * 4;

  Mat tr[T];

  void Pushup (int k) {
    tr[k] = tr[lc] * tr[rc];
  }

  void Update (int k, int x, Mat a, int L = 1, int R = n) {
    if (L == R) { 
      tr[k] = a;
      return;
    }
    int mid = (L + R) >> 1;
    if (x <= mid) {
      Update(lc, x, a, L, mid);
    }
    else {
      Update(rc, x, a, mid + 1, R);
    }
    Pushup(k);
  }

  Mat Query (int k, int l, int r, int L = 1, int R = n) {
    if (l <= L && r >= R) {
      return tr[k];
    }
    int mid = (L + R) >> 1;
    if (l > mid) { 
      return Query(rc, l, r, mid + 1, R);
    } 
    else if (r <= mid) {
      return Query(lc, l, r, L, mid);
    }
    else { 
      return Query(lc, l, r, L, mid) * Query(rc, l, r, mid + 1, R);
    }
  }

  #undef lc 
  #undef rc
}

void Calc (int u) {
  Mat ret = Seg::Query(1, dfn[u], dfn[bot[u]]);
  Vec rev = ret * Evec;
  f[u][0] = rev[0], f[u][1] = rev[1];
} 

void Build_mat (int u) {
  int d0 = (a[u] == 1 ? N : 0), d1 = (a[u] == 2 ? N : 0);
  mat[u] = Mat(g[u][0] + d0, g[u][0] + 1 + d0, g[u][1] + 1 + d1, g[u][1] + d1);
  Seg::Update(1, dfn[u], mat[u]);
}

void DP (int u) {
  for (auto v : e[u]) {
    if (v != fa[u] && v != wson[u]) {
      DP(v);
      g[u][0] += min(f[v][0], f[v][1] + 1);
      g[u][1] += min(f[v][1], f[v][0] + 1);
    }
  }
  if (wson[u]) { 
    DP(wson[u]);
  }
  Build_mat(u);
  if (tp[u] == u) {
    Calc(u);
  }
}

void Update_path (int u) {
  while (u) {
    Build_mat(u);
    int v = tp[u], tf[2] = {f[v][0], f[v][1]};
    Calc(v);
    u = fa[v];
    if (u) {
      g[u][0] += min(f[v][0], f[v][1] + 1) - min(tf[0], tf[1] + 1);
      g[u][1] += min(f[v][1], f[v][0] + 1) - min(tf[1], tf[0] + 1);
    }
  }
}

void initialize (int N, vector<int> A, vector<int> B) {
  n = N;
  for (int i = 0; i < n; ++i) {
    e[A[i]].push_back(B[i]);
    e[B[i]].push_back(A[i]);
  }
  Dfs(1), Dfs2(1, 1), DP(1);
}

int Get_ans () {
  return min(f[1][0], f[1][1]);
}

int cat (int v) {
  a[v] = 1, Update_path(v);
  return Get_ans();
}

int dog (int v) {
  a[v] = 2, Update_path(v);
  return Get_ans();
}

int neighbor (int v) {
  a[v] = 0, Update_path(v);
  return Get_ans();
}

// void Test () {
//   freopen("02-02.in", "r", stdin);
//   freopen("02-02.out", "w", stdout);
//   int N;
//   cin >> N;
//   vector<int> A(N), B(N);
//   for (int i = 0; i < N - 1; ++i) {
//     cin >> A[i] >> B[i];
//   }
//   initialize(N, A, B);
//   int Q;
//   cin >> Q;
//   for (int T, v; Q--; ) {
//     cin >> T >> v;
//     cout << (T == 1 ? cat : (T == 2 ? dog : neighbor))(v) << '\n';
//   }
// }

// int main () { Test(); }

T3 Collapse

\(n\) 座城鎮,一段電纜可以雙向連線兩個城鎮 \(u\)\(v\),接下來的 \(m\) 天中每一天恰好會建設或拆除一段電纜。
這些城鎮之間經常發生山體滑坡,若城鎮 \(x\)\(x + 1\) 發生山體滑坡,則對於形如 \((u, v)\) 的電纜(不妨設 \(u < v\)),若 \(u \le x\)\(v \ge x + 1\) 則該電纜不可用。
此時需要建立一些基站,你需要使得每個城鎮都可以透過若干電纜和一個基站連線。
現在給出 \(q\) 次詢問,每次詢問給出 \(t, x\),你需要求出若在第 \(t\) 天時城鎮 \(x\)\(x + 1\) 發生山體滑坡,最少要建立多少個基站、
\(1 \le n, q \le 10^5\)

首先重新描述題意:每條邊 \((u, v)\) 有出現時間 \([l, r]\),每次詢問在時間 \(t\),如果只保留 \([1, x]\)\([x + 1, n]\) 內部的邊,有多少連通塊。

顯然 \([1, x]\)\([x + 1, n]\) 兩個部分可以分別計算連通塊數再相加,並且兩部分是對偶的,所以我們下面只考慮計算 \([1, x]\)

我們對所有詢問按 \(t\) 排序,再對每 \(B\) 個詢問分一個塊,考慮對於每個塊如何算答案。

考察每條邊對這些詢問的貢獻,設這條邊的出現時間為 \([l, r]\),塊內詢問的時間段為 \([L, R]\)

\([l, r]\) 包含 \([L, R]\),則這條邊對整個塊都有貢獻,我們將所有這樣的邊按 \(y\) 排序,塊內的詢問按 \(x\) 排序,掃描線即可。時間複雜度 \(O(\frac{qm}{B} \log n)\)

\([l, r]\) 不包含但與 \([L, R]\),那麼對於每條邊只會落入這種情況 \(O(1)\) 次,此時我們在遇到一個詢問時暴力加入這一型別的邊,然後在撤銷回去即可,並查集需要按秩合併。時間複雜度 \(O(mB \log n)\)

總時間複雜度 \(O((\frac{qm}{B} + mB) \log n)\),程式碼中取 \(B = 333\)(隨便取的,沒仔細卡)。由於並查集和排序的 \(\log\) 常數很小,並且這題的加邊方式很難卡滿,可以透過。

Code
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <numeric>
#include <algorithm>
#include "collapse.h"

using namespace std;
using Pii = pair<int, int>; 

const int N = 1e5 + 5, B = 333;

int n, m, q;
int fa[N], p[N];
set<Pii> es;
map<Pii, int> mp;

struct E {
  int x, y, l, r;

  // One edge connected x and y with time [l, r]
}; 

struct Q {
  int t, p, id;
}; 

struct Union_find {
  int fa[N], d[N]; 
  vector<Pii> v; 

  void Init () {
    iota(fa + 1, fa + n + 1, 1);
    fill(d + 1, d + n + 1, 0);
    v.clear();
  }

  int Find (int x) {
    return fa[x] == x ? x : Find(fa[x]);
  } 

  void Unite (int x, int y) {
    x = Find(x), y = Find(y);
    if (d[x] < d[y]) swap(x, y);
    v.push_back({y, d[x]});
    fa[y] = x; 
    d[x] = max(d[x], d[y] + 1);
  }

  bool Try_to_unite (int x, int y) {
    x = Find(x), y = Find(y);
    if (x == y) return 0;  
    Unite(x, y); 
    return 1;
  }

  int Get_time () {
    return v.size();
  }

  void Roll_back (int t) {
    while (v.size() > t) {
      Pii t = v.back();
      v.pop_back();
      d[fa[t.first]] = t.second;
      fa[t.first] = t.first;
    }
  }
} uf;

vector<E> ev; 

int Find (int x) {
  if (fa[x] == x) return x;
  return fa[x] = Find(fa[x]);
} 

vector<int> simulateCollapse (int N, vector<int> T, vector<int> X, vector<int> Y, vector<int> W, vector<int> P) {
  [&]() -> void {
    // Remake
    n = N, m = T.size(), q = W.size();
    for (auto &x : X) ++x;
    for (auto &y : Y) ++y; 
    for (auto &w : W) ++w;
    for (auto &p : P) ++p;
    for (int i = 0; i < m; ++i) {
      if (X[i] > Y[i]) 
        swap(X[i], Y[i]);
    }
    for (int i = 0; i < m; ++i) {
      int x = X[i], y = Y[i];
      if (!T[i]) {
        mp.insert({{x, y}, i + 1});
      }
      else {
        auto it = mp.find({x, y});
        ev.push_back(E({x, y, it->second, i}));
        mp.erase(it);
      }
    }
    for (auto i : mp) {
      ev.push_back(E({i.first.first, i.first.second, i.second, m})); 
    }
    
    // cout << "Edge set : " << '\n';
    // for (auto i : ev) {
    //   cout << i.x << ' ' << i.y << ' ' << i.l << ' ' << i.r << '\n';
    // }
    // cout << "-------------" << '\n';
    // exit(0);
  }();
  iota(p, p + q, 0);
  sort(p, p + q, [&](int i, int j) -> bool {
    return W[i] < W[j];
  });
  vector<int> ans(q);
  for (int tl = 0, tr; tl < q; tl = tr + 1) {
    tr = min(tl + B - 1, q - 1);
    int L = W[p[tl]], R = W[p[tr]];
    vector<Pii> cover;
    vector<E> vec; 
    for (auto e : ev) {
      if (e.l <= L && e.r >= R) {
        cover.push_back({e.x, e.y});
      }
      else if (R >= e.l && e.r >= L) {
        vec.push_back(e);
      }
    }
    sort(cover.begin(), cover.end(), [&](Pii a, Pii b) -> bool {
      return a.second < b.second;
    });
    vector<Q> qv;
    for (int i = tl; i <= tr; ++i) {
      int id = p[i];
      qv.push_back(Q({W[id], P[id], id}));
    }
    sort(qv.begin(), qv.end(), [&](Q a, Q b) -> bool {
      return a.p < b.p;
    });
    auto cur = cover.begin();
    uf.Init();
    int ucnt = 0;
    // The times of successful merge
    for (auto q : qv) {
      int p = q.p, t = q.t, id = q.id;
      while (cur != cover.end() && cur->second <= p) {
        ucnt += uf.Try_to_unite(cur->first, cur->second);
        ++cur;
      }
      int timel = uf.Get_time(), tmpcnt = 0; 
      for (auto e : vec) {
        if (e.l <= t && e.r >= t && e.y <= p) {
          tmpcnt += uf.Try_to_unite(e.x, e.y);
        }
      }
      ans[q.id] += p - ucnt - tmpcnt;
      uf.Roll_back(timel);
    }
    reverse(qv.begin(), qv.end());
    sort(cover.begin(), cover.end(), [&](Pii a, Pii b) -> bool {
      return a.first > b.first;
    });
    cur = cover.begin();
    uf.Init();
    ucnt = 0; 
    for (auto q : qv) {
      int p = q.p, t = q.t, id = q.id;
      while (cur != cover.end() && cur->first > p) {
        ucnt += uf.Try_to_unite(cur->first, cur->second);
        ++cur;
      }
      int timel = uf.Get_time(), tmpcnt = 0;
      for (auto e : vec) {
        if (e.l <= t && e.r >= t && e.x > p) {
          tmpcnt += uf.Try_to_unite(e.x, e.y);
        }
      }
      ans[q.id] += (n - p) - ucnt - tmpcnt;
      uf.Roll_back(timel);
    }
  }
  return ans;
}

// void Test () {
//   int N, C, Q;
//   cin >> N >> C >> Q;
//   vector<int> T, X, Y;
//   for (int i = 0, t, x, y; i < C; ++i) {
//     cin >> t >> x >> y;
//     T.push_back(t), X.push_back(x), Y.push_back(y);
//   }
//   vector<int> W, P;
//   for (int i = 0, w, p; i < Q; ++i) {
//     cin >> w >> p;
//     W.push_back(w), P.push_back(p);
//   }
//   vector<int> ans = simulateCollapse(N, T, X, Y, W, P);
//   for (auto i : ans) {
//     cout << i << '\n';
//   }
// }

// int main () { Test(); }

T4 Xylophone

題意:你要猜一個隱藏的 \(1 \sim n\) 的排列 \(p\),保證 \(p\) 的最小值出現在最大值左邊。
你可以向互動庫提出若干次詢問,每次詢問你可以給出一個區間 \([l, r]\),互動庫會返回 \(p_l, p_{l + 1}, \ldots, p_r\) 中最大數和最小數之差。
你需要透過不超過 \(10^4\) 詢問得到排列 \(p\)
\(n \le 5 \times 10^3\)

考慮先對於每個 \(i \in [2, n]\),詢問 \((i, i - 1)\),令 \(b_i = p_i - p_{i - 1}\),此時我們就得到了 \(|b_2|, |b_3|, \ldots, |b_n|\)

假設我們透過某種方式得到了 \(b_2, b_3, \ldots, b_n\),那麼這道題就做完了。考慮隨便欽定一個 \(p_1\),我們就可以得到 \(p_1, p_2, \ldots, p_n\) 的相對關係,那麼 \(p\) 構成一個 \(x + 1, x + 2, \ldots, x + n\) 的排列,將整個排列偏移 \(x\) 即可還原 \(p\)

考慮如何還原出 \(p\)。我們進一步 \(\forall i \in [3, n]\),詢問 \((i - 2, i)\),那麼我們就得到了 \(c_i = \max(|b_i|, |b_{i - 1}|, |b_{i - 1} + b_i|)\),因為 \(b_i > 0\),所以 \(b_{i - 1}\)\(b_i\) 同號當且僅當 \(c_i = |b_i| + |b_{i - 1}|\)。此時我們還原出了 \(i \in [3, n]\)\(b_{i - 1}\)\(b_i\) 符號的相對關係。

此時我們只需列舉 \(b_2 = -1/1\) 即可得到 \(b_3, b_4, \ldots, b_n\)。此時我們還原可以得到兩個 \(a\),此時滿足最小值出現在最大值左側的就是真正的 \(a\)

為什麼恰有一個合法?考慮對於兩個 \(a\),將所有點 \((i, a_i)\) 畫在二維平面上,那麼每次 \(i\)\(i + 1\) 移動的過程,兩個序高度的變化量是相反的,所以我們最終會得到兩條形態對偶的折線。那麼它們滿足一條折線的峰值恰是另一條折線的谷值,所以一定是恰好一個滿足條件。

時間複雜度 \(O(n)\)。詢問次數 \(2n - 3\)

Code
#include "xylophone.h"
#include <iostream>
#include <algorithm>

using namespace std;

void solve (int N);
int query (int s, int t);
void answer (int i, int a);

const int kN = 5e3 + 5; 

int a[kN], b[kN], sgn[kN], c[kN], e[kN];

void solve (int N) {
  for (int i = 2; i <= N; ++i) {
    b[i] = query(i - 1, i);
  }
  for (int i = 3; i <= N; ++i) {
    c[i] = query(i - 2, i);
  }
  for (int i = 3; i <= N; ++i) {
    sgn[i] = (c[i] == b[i] + b[i - 1] ? 1 : -1);
  }
  for (int o = 0; o < 2; ++o) {
    e[2] = (!o ? -1 : 1); 
    for (int i = 3; i <= N; ++i) {
      e[i] = e[i - 1] * sgn[i];
    }
    for (int i = 2; i <= N; ++i) {
      e[i] *= b[i];
    }
    a[1] = 0; 
    for (int i = 2; i <= N; ++i) { 
      a[i] = a[i - 1] + e[i];
    }
    int del = -*min_element(a + 1, a + N + 1) + 1;
    for (int i = 1; i <= N; ++i) {
      a[i] += del;
    }
    int mi = min_element(a + 1, a + N + 1) - a, mx = max_element(a + 1, a + N + 1) - a; 
    if (mi < mx) {
      for (int i = 1; i <= N; ++i) {
        answer(i, a[i]);
      }
      return; 
    }
  }
}

相關文章