Problem:
交易計画 (Trade Plan)
問題文
JOI 合衆国には
1
から
N
までの番号が付けられた
N
個の都市と，
1
から
M
までの番号が付けられた
M
本の道路がある．道路
i
(
1 ≦ i ≦ M
) は，都市
U
i
と都市
V
i
を双方向に結んでいる．
JOI 合衆国は
1
から
K
までの番号が付けられた
K
個の州からなる．都市
j
(
1 ≦ j ≦ N
) は州
S
j
に属している．また，どの州も少なくとも
1
つの都市を含む．
JOI 合衆国の産業大臣である K 理事長は，これから
Q
回の交易を行いたいと考えている．
k
番目の交易 (
1 ≦ k ≦ Q
) は，都市
A
k
から都市
B
k
にいくつかの道路や都市を通って特産品を輸送するというものである．ただし，この交易に協力してくれるのは州
S
A
k
と 州
S
B
k
のみ (
S
A
k
= S
B
k
の場合は州
S
A
k
のみ) であり，これらの州に属していない都市を通ると特産品は盗まれてしまう．
K 理事長は特産品が盗まれないように交易を行うような輸送経路があるのかを調べたい．都市と道路の配置，州と交易の情報が与えられたとき，各交易について特産品を無事届けることが可能かを判定するプログラムを作成せよ．
制約
2 ≦ N ≦ 400 000
．
1 ≦ M ≦ 400 000
．
1 ≦ K ≦ N
．
1 ≦ U
i
＜ V
i
≦ N
(
1 ≦ i ≦ M
)．
(U
i
, V
i
) ≠ (U
j
, V
j
)
(
1 ≦ i ＜ j ≦ M
)．
1 ≦ S
j
≦ K
(
1 ≦ j ≦ N
)．
すべての
l
(
1 ≦ l ≦ K
) について，
S
j
= l
となる
j
(
1 ≦ j ≦ N)
が存在する．
1 ≦ Q ≦ 400 000
．
1 ≦ A
k
≦ N
(
1 ≦ k ≦ Q
)．
1 ≦ B
k
≦ N
(
1 ≦ k ≦ Q
)．
A
k
≠ B
k
(
1 ≦ k ≦ Q
)．
入力される値はすべて整数である．
入力
入力は以下の形式で標準入力から与えられる．
N
M
K
U
1
V
1
U
2
V
2
:
U
M
V
M
S
1
S
2
…
S
N
Q
A
1
B
1
A
2
B
2
:
A
Q
B
Q
出力
標準出力に
Q
行で出力せよ．
k
行目 (
1 ≦ k ≦ Q
) には，
k
番目の交易において特産品を届けることが可能であれば
1
を，不可能であれば
0
を出力せよ．
入出力例
入力例 1
4 3 2
1 2
2 3
3 4
1 2 1 2
3
1 2
1 3
1 4
出力例 1
1
0
1
1
番目の交易は，州
1
または州
2
に属する都市のみを通って，都市
1
から都市
2
に特産品を輸送するというものである．都市
1
→ 都市
2
と輸送すれば条件を満たすので，
1
を出力する．
2
番目の交易は，州
1
に属する都市のみを通って，都市
1
から都市
3
に特産品を輸送するというものである．条件を満たす輸送経路は存在しないので，
0
を出力する．
3
番目の交易は，州
1
または州
2
に属する都市のみを通って，都市
1
から都市
4
に特産品を輸送するというものである．都市
1
→ 都市
2
→ 都市
3
→ 都市
4
と輸送すれば条件を満たすので，
1
を出力する．
入力例 2
4 2 1
1 3
2 4
1 1 1 1
4
1 2
1 3
2 3
2 4
出力例 2
0
1
0
1
入力例 3
6 5 3
1 2
3 4
5 6
1 4
3 5
1 1 2 2 3 3
4
1 4
1 5
3 6
4 3
出力例 3
1
0
1
1
入力例 4
8 11 3
4 8
1 8
4 6
3 5
2 4
7 8
6 7
3 4
1 4
2 3
3 8
2 3 1 1 2 1 2 1
10
8 2
8 1
2 7
5 3
5 7
4 8
1 8
6 8
6 5
1 8
出力例 4
1
1
0
1
0
1
1
1
1
1
情報オリンピック日本委員会
作 『第 21 回日本情報オリンピック JOI 2021/2022 二次予選競技課題』

Submitted solutions:

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define ll long long
#define db double
#define gcd(a,b) __gcd(a,b)
#define lcm(a,b) a*b / __gcd(a,b)
#define I first
#define II second
#define pb push_back
#define ii pair<int,int>
const int INF = 2 * 1e9;
const int N = 4e5 + 1;
const int MOD = 1e9 + 7;
int n, m, k, ans[N], cur, s[N];
array<int, 4> e[N];
struct DSU
{
    vector<int> par, sz;
    vector<array<int, 4> > rb;
    void rz(int n)
    {
        par.assign(n + 1, 0);
        sz.assign(n + 1, 1);
        rb.clear();
        cur = 1;
    }
    int get(int u)
    {
        return (!par[u] ? u : get(par[u]));
    }
    int join(int u, int v)
    {
        u = get(u), v = get(v);
        if (u == v) return 0;
        if (sz[u] < sz[v]) swap(u, v);
        rb.pb({u, sz[u], v, cur});
        sz[u] += sz[v];
        par[v] = u;
        cur = max(cur, sz[u]);
        return 1;
    }
    void rollback(int cnt)
    {
        while (cnt--)
        {
            auto G = rb.back(); rb.pop_back();
            sz[G[0]] = G[1];
            par[G[2]] = 0;
            cur = G[3];
        }
    }
} T;
int32_t main()
{
#define TASKNAME ""
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    if (fopen(TASKNAME".inp","r" ))
    {
        freopen(TASKNAME".inp","r",stdin);
        freopen(TASKNAME".out","w",stdout);
    }
    cin >> n >> m >> k;
    T.rz(n);
    for (int i = 1; i <= m; i++) cin >> e[i][2] >> e[i][3];
    for (int i = 1; i <= n; i++) cin >> s[i];
    for (int i = 1; i <= m; i++)
    {
        if (s[e[i][2]] > s[e[i][3]]) swap(e[i][2], e[i][3]);
        e[i][0] = s[e[i][2]];
        e[i][1] = s[e[i][3]];
        if (e[i][0] == e[i][1]) T.join(e[i][2], e[i][3]);
    }
    sort(e + 1, e + m + 1);
    int q; cin >> q;
    vector<array<int, 5> > Query;
    for (int i = 1; i <= q; i++)
    {
        int x, y;
        cin >> x >> y;
        if (s[x] > s[y]) swap(x, y);
        Query.pb({s[x], s[y], x, y, i});
    }
    sort(Query.begin(), Query.end());
    for (int i = 0; i < q; i++)
    {
        if (Query[i][0] == Query[i][1])
        {
            ans[Query[i][4]] = (T.get(Query[i][2]) == T.get(Query[i][3]));
            continue;
        }
        int j = i;
        while (j + 1 < q && Query[j + 1][0] == Query[i][0] && Query[j + 1][1] == Query[i][1]) j++;
        int l = lower_bound(e + 1, e + m + 1, array<int, 4>{Query[i][0], Query[i][1], 0, 0}) - e;
        if (l == m + 1)
        {
            i = j;
            continue;
        }
        int cnt = 0;
        for (int k = l; k <= m; k++)
            if (e[k][0] == Query[i][0] && e[k][1] == Query[i][1])
            cnt += T.join(e[k][2], e[k][3]); else break;
        for (int k = i; k <= j; k++)
            ans[Query[k][4]] = (T.get(Query[k][2]) == T.get(Query[k][3]));
        T.rollback(cnt);
        i = j;
    }
    for (int i = 1; i <= q; i++) cout << ans[i] << '\n';
    return 0;
}
accuracy: 1
time_ms: 230
memory_kb: 40320
final_rank: 1

code_snippet: #line 1 "icpc.cpp"
#include <bits/stdc++.h>
using namespace std;

#line 2 "Library/DataStructure/RollbackUnionFind.hpp"

/**
 * @file RollbackUnionFind.hpp
 * @author lX57
 * @brief Rollback Union-Find - Rollback 可能 Union-Find
 */

#line 2 "Library/Common.hpp"

/**
 * @file Common.hpp
 */

#line 26 "Library/Common.hpp"
using namespace std;
#line 10 "Library/DataStructure/RollbackUnionFind.hpp"

class RollbackUnionFind{
    public:
    /**
     * @brief 要素数 `size` で Union-Find を初期化します。
     * @note 要素番号は 0-index です。
     * @param size 要素数
     */
    RollbackUnionFind(size_t size) : data_(size, -1){}

    /**
     * @brief 要素 `k` が属する集合の代表元を返します。
     * @param k 探索する要素
     * @return 要素 `k` が属する集合の代表元
     */
    int Find(const int k) const {
        if(data_[k] < 0) return k;
        return Find(data_[k]);
    }

    /**
     * @brief 要素 `x` と要素 `y` が同じ集合に属しているかを判定します。
     * @param x 要素番号
     * @param y 要素番号
     * @return 同じ集合に属しているか
     */
    bool Same(const int x, const int y) const {
        return Find(x) == Find(y);
    }

    /**
     * @brief 要素 `x` と要素 `y` を併合します。併合した場合は `true` を返します。
     * @param x 要素番号
     * @param y 要素番号
     * @return 併合を行ったか
     */
    bool Unite(int x, int y){
        x = Find(x), y = Find(y);
        history_.push({x, data_[x]});
        history_.push({y, data_[y]});
        if(x == y) return false;
        if(data_[x] > data_[y]) swap(x, y);
        data_[x] += data_[y];
        data_[y] = x;
        return true;
    }

    /**
     * @brief 現在の状態を保存します。別の状態を保存している場合、上書きします。
     */
    void Record(){
        record_ = history_.size();
    }

    /**
     * @brief 直前の `Unite` 操作を取り消します。
     */
    void Undo(){
        auto [y, dy] = history_.top();
        history_.pop();
        data_[y] = dy;
        auto [x, dx] = history_.top();
        history_.pop();
        data_[x] = dx;
    }

    /**
     * @brief `Record` で記録した状態までロールバックします。
     */
    void Rollback(){
        int state = record_;
        while(state < (int)history_.size()) Undo();
    }

    /**
     * @brief 各集合に所属する要素を列挙します。
     * @return vector<vector<int>> 各集合に所属する要素の一覧
     */
    vector<vector<int>> Group() const {
        vector<vector<int>> ret(data_.size());
        for(int i = 0; i < data_.size(); ++i){
            ret[Find(i)].emplace_back(i);
        }
        ret.erase(remove_if(begin(ret), end(ret), [&](vector<int> &v){
            return v.empty();
        }), end(ret));
        return ret;
    }

    /**
     * @brief 要素 `k` が属する集合の要素数を求めます。
     * @param k 要素番号
     * @return int 集合の要素数
     */
    int Size(const int k) const {
        return -data_[Find(k)];
    }

    private:
    vector<int> data_;
    stack<pair<int, int>> history_;
    int record_;
};
#line 5 "icpc.cpp"

int main(){
    int N, M, K; cin >> N >> M >> K;
    vector<int> U(M), V(M);
    for(int i = 0; i < M; ++i) cin >> U[i] >> V[i], --U[i], --V[i];
    vector<int> S(N);
    for(int i = 0; i < N; ++i) cin >> S[i], --S[i];

    RollbackUnionFind uf(N);
    map<pair<int, int>, vector<pair<int, int>>> Road;
    for(int i = 0; i < M; ++i){
        int u = U[i], v = V[i];
        int su = S[u], sv = S[v];
        if(su == sv) uf.Unite(u, v);
        else{
            if(su > sv) swap(su, sv);
            Road[{su, sv}].push_back({u, v});
        }
    }
    int Q; cin >> Q;
    map<pair<int, int>, vector<tuple<int, int, int>>> Query;
    vector<int> ans(Q, -1);
    for(int i = 0; i < Q; ++i){
        int A, B; cin >> A >> B, --A, --B;
        int sa = S[A], sb = S[B];
        if(sa == sb){
            ans[i] = uf.Same(A, B);
        }
        else{
            if(sa > sb) swap(sa, sb);
            Query[{sa, sb}].push_back({i, A, B});
        }
    }
    uf.Record();
    for(auto [state_pair, qs] : Query){
        for(auto [u, v] : Road[state_pair]){
            uf.Unite(u, v);
        }
        for(auto [i, a, b] : qs){
            ans[i] = uf.Same(a, b);
        }
        uf.Rollback();
    }
    for(auto a : ans){
        assert(a != -1);
        cout << a << endl;
    }
}
accuracy: 1
time_ms: 910
memory_kb: 129920
final_rank: 8

code_snippet: #line 1 "test/data-structure/undo_dsu.test.2.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/0723"

#line 1 "test/template.hpp"
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = LLONG_MAX / 4;
#define all(a) begin(a), end(a)
#line 1 "src/data-structure/undo_dsu.hpp"
// base: edf246
struct undo_dsu {
   private:
   int _n;
   vector<int> p;
   stack<pair<int, int>> history;

   public:
   undo_dsu() : _n(0) {}
   explicit undo_dsu(int n) : _n(n), p(n, -1) {}

   int merge(int a, int b) {
      // assert(0 <= a && a < _n);
      // assert(0 <= b && b < _n);
      int x = leader(a), y = leader(b);
      if(x == y) {
         history.emplace(x, p[x]);
         history.emplace(y, p[y]);
         return x;
      }
      if(-p[x] < -p[y]) swap(x, y);
      history.emplace(x, p[x]);
      history.emplace(y, p[y]);
      p[x] += p[y];
      p[y] = x;
      return x;
   }

   bool same(int a, int b) {
      // assert(0 <= a && a < _n);
      // assert(0 <= b && b < _n);
      return leader(a) == leader(b);
   }

   int leader(int a) {
      // assert(0 <= a && a < _n);
      while(p[a] >= 0) a = p[a];
      return a;
   }

   void undo() {
      p[history.top().first] = history.top().second;
      history.pop();
      p[history.top().first] = history.top().second;
      history.pop();
   }

   int snapshot() { return history.size(); }

   void rollback(int snapshot = 0) {
      while(history.size() > snapshot) undo();
   }

   int size(int a) {
      // assert(0 <= a && a < _n);
      return -p[leader(a)];
   }  // 818fe7
};
#line 5 "test/data-structure/undo_dsu.test.2.cpp"

int main() {
   cin.tie(0)->sync_with_stdio(0);
   int N, M, K;
   cin >> N >> M >> K;
   vector<pair<int, int>> edges(M);
   for(int i = 0; i < M; i++) {
      int u, v;
      cin >> u >> v;
      u--;
      v--;
      edges[i] = {u, v};
   }

   vector<int> S(N);
   for(auto&& s : S) cin >> s;
   map<pair<int, int>, vector<pair<int, int>>> E;
   undo_dsu dsu(N);
   for(auto [u, v] : edges) {
      int su = S[u], sv = S[v];
      if(su == sv) {
         dsu.merge(u, v);
      } else {
         if(su > sv) {
            swap(u, v);
            swap(su, sv);
         }
         E[{su, sv}].push_back({u, v});
      }
   }
   int snapshot = dsu.snapshot();
   int Q;
   cin >> Q;
   vector<int> ans(Q);
   map<pair<int, int>, vector<int>> q;
   vector<pair<int, int>> query(Q);

   for(int i = 0; i < Q; i++) {
      int A, B;
      cin >> A >> B;
      A--;
      B--;
      if(S[A] > S[B]) swap(A, B);
      int SA = A;
      int SB = B;
      query[i] = {A, B};
      q[{S[A], S[B]}].push_back(i);
   }

   for(auto [key, value] : q) {
      auto [su, sv] = key;
      for(auto [u, v] : E[{su, sv}]) { dsu.merge(u, v); }
      for(auto i : value) {
         auto [A, B] = query[i];
         ans[i] = dsu.same(A, B);
      }
      dsu.rollback(snapshot);
   }
   for(auto&& a : ans) cout << a << '\n';
   return 0;
}
accuracy: 1
time_ms: 680
memory_kb: 132832
final_rank: 6

code_snippet: #include<bits/stdc++.h>
using namespace std;
using ll = long long;

struct Undo_dsu{
    vector<int> data;
    stack<pair<int,int>> hist;
    Undo_dsu(int sz){data.assign(sz,-1);}

    bool merge(int x,int y){
        x=find(x),y=find(y);
        hist.emplace(x,data[x]);
        hist.emplace(y,data[y]);
        if(x==y)return false;
        if(data[x]>data[y])swap(x,y);
        data[x]+=data[y];
        data[y]=x;
        return true;
    }

    int same(int x,int y){return find(x)==find(y);}
    int size(int x){return (-data[find(x)]);}

    void undo(){
        data[hist.top().first] = hist.top().second;
        hist.pop();
        data[hist.top().first] = hist.top().second;
        hist.pop();
    }

    int find(int k){
        if(data[k]<0)return k;
        return find(data[k]);
    }
};


int main() {

    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    ll N,M,K;
    cin>>N>>M>>K;
    vector<pair<int,int>> E(M);
    for(int i=0;i<M;i++){
        int u,v;
        cin>>u>>v;
        E[i]={u-1,v-1};
    }
    vector<int> S(N);
    for(int i=0;i<N;i++){
        cin>>S[i];
        S[i]--;
    }
    map<pair<int,int>,vector<pair<int,int>>> MP,query;
    Undo_dsu D(N);
    for(int i=0;i<M;i++){
        int u=E[i].first;
        int v=E[i].second;
        if(S[u]==S[v]){
            D.merge(u,v);
            continue;
        }
        if(S[u]>S[v])swap(u,v);
        MP[{S[u],S[v]}].push_back({u,v});
    }
    int Q;
    cin>>Q;
    vector<bool> AN(Q,0);
    set<pair<int,int>> ANM;
    vector<pair<int,int>> QR(Q);
    for(int i=0;i<Q;i++){
        int a,b;
        cin>>a>>b;
        a--;b--;
        QR[i]={a,b};
        if(S[a]==S[b]){
            AN[i]=D.same(a,b);
        }
        else{
            if(S[a]>S[b])swap(a,b);
            QR[i]={a,b};
            query[{S[a],S[b]}].push_back({a,b});
        }
    }
    for(auto [p,V]:query){
        for(auto [u,v]:MP[p]){
            D.merge(u,v);
        }
        for(auto [u,v]:V){
            if(D.same(u,v)){
                ANM.insert({u,v});
            }
        }
        for(int i=0;i<MP[p].size();i++)D.undo();
    }
    for(int i=0;i<Q;i++){
        if(S[QR[i].first]==S[QR[i].second])cout<<(AN[i])<<"\n";
        else if(ANM.count(QR[i]))cout<<1<<"\n";
        else cout<<0<<"\n";
    }
}
accuracy: 1
time_ms: 700
memory_kb: 131088
final_rank: 7

code_snippet: #line 1 "a.cpp"
#define PROBLEM ""
#line 2 "/home/kuhaku/home/github/algo/lib/template/template.hpp"
#pragma GCC target("sse4.2,avx2,bmi2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
template <class T, class U>
constexpr bool chmax(T &a, const U &b) {
    return a < (T)b ? a = (T)b, true : false;
}
template <class T, class U>
constexpr bool chmin(T &a, const U &b) {
    return (T)b < a ? a = (T)b, true : false;
}
constexpr std::int64_t INF = 1000000000000000003;
constexpr int Inf = 1000000003;
constexpr double EPS = 1e-7;
constexpr double PI = M_PI;
#line 3 "/home/kuhaku/home/github/algo/lib/template/macro.hpp"
#define FOR(i, m, n) for (int i = (m); i < int(n); ++i)
#define FORR(i, m, n) for (int i = (m)-1; i >= int(n); --i)
#define FORL(i, m, n) for (int64_t i = (m); i < int64_t(n); ++i)
#define rep(i, n) FOR (i, 0, n)
#define repn(i, n) FOR (i, 1, n + 1)
#define repr(i, n) FORR (i, n, 0)
#define repnr(i, n) FORR (i, n + 1, 1)
#define all(s) (s).begin(), (s).end()
#line 3 "/home/kuhaku/home/github/algo/lib/template/sonic.hpp"
struct Sonic {
    Sonic() {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        std::cout << std::fixed << std::setprecision(20);
    }

    constexpr void operator()() const {}
} sonic;
#line 5 "/home/kuhaku/home/github/algo/lib/template/atcoder.hpp"
using namespace std;
using ll = std::int64_t;
using ld = long double;
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
    return is >> p.first >> p.second;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (T &i : v) is >> i;
    return is;
}
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
    return os << '(' << p.first << ',' << p.second << ')';
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
    for (auto it = v.begin(); it != v.end(); ++it) {
        os << (it == v.begin() ? "" : " ") << *it;
    }
    return os;
}
template <class Head, class... Tail>
void co(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cout << head << '\n';
    else std::cout << head << ' ', co(std::forward<Tail>(tail)...);
}
template <class Head, class... Tail>
void ce(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cerr << head << '\n';
    else std::cerr << head << ' ', ce(std::forward<Tail>(tail)...);
}
template <typename T, typename... Args>
auto make_vector(T x, int arg, Args... args) {
    if constexpr (sizeof...(args) == 0) return std::vector<T>(arg, x);
    else return std::vector(arg, make_vector<T>(x, args...));
}
void Yes(bool is_correct = true) {
    std::cout << (is_correct ? "Yes" : "No") << '\n';
}
void No(bool is_not_correct = true) {
    Yes(!is_not_correct);
}
void YES(bool is_correct = true) {
    std::cout << (is_correct ? "YES" : "NO") << '\n';
}
void NO(bool is_not_correct = true) {
    YES(!is_not_correct);
}
void Takahashi(bool is_correct = true) {
    std::cout << (is_correct ? "Takahashi" : "Aoki") << '\n';
}
void Aoki(bool is_not_correct = true) {
    Takahashi(!is_not_correct);
}
#line 2 "/home/kuhaku/home/github/algo/lib/tree/undo_union_find.hpp"

/**
 * @brief Undo可能Union-Find
 * @details Implement (union by size)
 *
 * @see https://ei1333.github.io/luzhiled/snippets/structure/union-find.html
 */
struct undo_union_find {
    undo_union_find() : data(), history() {}
    undo_union_find(int _n) : data(_n, -1), history() {}

    int root(int x) { return this->data[x] < 0 ? x : this->root(this->data[x]); }
    int get_root(int k) { return this->root(k); }

    bool is_root(int k) const { return this->data[k] < 0; }

    bool same(int x, int y) { return this->root(x) == this->root(y); }
    bool is_same(int x, int y) { return this->same(x, y); }

    int size(int k) { return -this->data[this->root(k)]; }
    int get_size(int k) { return this->size(k); }

    bool unite(int x, int y) {
        x = this->root(x), y = this->root(y);
        this->history.emplace(x, this->data[x]);
        this->history.emplace(y, this->data[y]);
        if (x == y) return false;
        if (this->data[x] > this->data[y]) std::swap(x, y);
        this->data[x] += this->data[y];
        this->data[y] = x;
        return true;
    }

    void undo() {
        this->data[this->history.top().first] = this->history.top().second;
        this->history.pop();
        this->data[this->history.top().first] = this->history.top().second;
        this->history.pop();
    }

    int snapshot() const { return this->history.size(); }

    void rollback(int x = 0) {
        while ((int)(this->history.size()) > x) this->undo();
    }

  private:
    std::vector<int> data;
    std::stack<std::pair<int, int>> history;
};
#line 4 "a.cpp"

int main(void) {
    int n, m, k;
    cin >> n >> m >> k;
    vector<pair<int, int>> edges(m);
    cin >> edges;
    for (auto &[x, y] : edges) --x, --y;
    vector<int> s(n);
    cin >> s;
    undo_union_find uf(n);
    map<pair<int, int>, vector<int>> mp;
    rep (i, m) {
        auto [x, y] = edges[i];
        if (s[x] == s[y]) {
            uf.unite(x, y);
        } else {
            if (s[x] > s[y])
                swap(x, y);
            mp[{s[x], s[y]}].emplace_back(i);
        }
    }

    map<pair<int, int>, vector<int>> pm;
    int q;
    cin >> q;
    vector<pair<int, int>> queries(q);
    cin >> queries;
    for (auto &[x, y] : queries) --x, --y;
    vector<int> ans(q, -1);
    rep (i, q) {
        auto [x, y] = queries[i];
        if (s[x] == s[y]) {
            ans[i] = uf.same(x, y);
        } else {
            if (s[x] > s[y])
                swap(x, y);
            pm[{s[x], s[y]}].emplace_back(i);
        }
    }

    int ss = uf.snapshot();
    for (auto &[p, v] : pm) {
        for (auto e : mp[p]) {
            uf.unite(edges[e].first, edges[e].second);
        }
        for (auto e : v) {
            ans[e] = uf.same(queries[e].first, queries[e].second);
        }
        uf.rollback(ss);
    }

    rep (i, q) co(ans[i]);

    return 0;
}
accuracy: 1
time_ms: 680
memory_kb: 132624
final_rank: 5

code_snippet: #line 1 "a.cpp"
#define PROBLEM ""
#line 2 "/home/kuhaku/home/github/algo/lib/template/template.hpp"
#pragma GCC target("sse4.2,avx2,bmi2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
template <class T, class U>
constexpr bool chmax(T &a, const U &b) {
    return a < (T)b ? a = (T)b, true : false;
}
template <class T, class U>
constexpr bool chmin(T &a, const U &b) {
    return (T)b < a ? a = (T)b, true : false;
}
constexpr std::int64_t INF = 1000000000000000003;
constexpr int Inf = 1000000003;
constexpr double EPS = 1e-7;
constexpr double PI = M_PI;
#line 3 "/home/kuhaku/home/github/algo/lib/template/macro.hpp"
#define FOR(i, m, n) for (int i = (m); i < int(n); ++i)
#define FORR(i, m, n) for (int i = (m)-1; i >= int(n); --i)
#define FORL(i, m, n) for (int64_t i = (m); i < int64_t(n); ++i)
#define rep(i, n) FOR (i, 0, n)
#define repn(i, n) FOR (i, 1, n + 1)
#define repr(i, n) FORR (i, n, 0)
#define repnr(i, n) FORR (i, n + 1, 1)
#define all(s) (s).begin(), (s).end()
#line 3 "/home/kuhaku/home/github/algo/lib/template/sonic.hpp"
struct Sonic {
    Sonic() {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        std::cout << std::fixed << std::setprecision(20);
    }

    constexpr void operator()() const {}
} sonic;
#line 5 "/home/kuhaku/home/github/algo/lib/template/atcoder.hpp"
using namespace std;
using ll = std::int64_t;
using ld = long double;
template <class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {
    return is >> p.first >> p.second;
}
template <class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {
    for (T &i : v) is >> i;
    return is;
}
template <class T, class U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &p) {
    return os << '(' << p.first << ',' << p.second << ')';
}
template <class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
    for (auto it = v.begin(); it != v.end(); ++it) {
        os << (it == v.begin() ? "" : " ") << *it;
    }
    return os;
}
template <class Head, class... Tail>
void co(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cout << head << '\n';
    else std::cout << head << ' ', co(std::forward<Tail>(tail)...);
}
template <class Head, class... Tail>
void ce(Head &&head, Tail &&...tail) {
    if constexpr (sizeof...(tail) == 0) std::cerr << head << '\n';
    else std::cerr << head << ' ', ce(std::forward<Tail>(tail)...);
}
template <typename T, typename... Args>
auto make_vector(T x, int arg, Args... args) {
    if constexpr (sizeof...(args) == 0) return std::vector<T>(arg, x);
    else return std::vector(arg, make_vector<T>(x, args...));
}
void Yes(bool is_correct = true) {
    std::cout << (is_correct ? "Yes" : "No") << '\n';
}
void No(bool is_not_correct = true) {
    Yes(!is_not_correct);
}
void YES(bool is_correct = true) {
    std::cout << (is_correct ? "YES" : "NO") << '\n';
}
void NO(bool is_not_correct = true) {
    YES(!is_not_correct);
}
void Takahashi(bool is_correct = true) {
    std::cout << (is_correct ? "Takahashi" : "Aoki") << '\n';
}
void Aoki(bool is_not_correct = true) {
    Takahashi(!is_not_correct);
}
#line 3 "/home/kuhaku/home/github/algo/lib/template/vector.hpp"

struct increment_impl {
    template <class T>
    const increment_impl &operator>>(std::vector<T> &v) const {
        for (auto &x : v) ++x;
        return *this;
    }
} Inc;

struct decrement_impl {
    template <class T>
    const decrement_impl &operator>>(std::vector<T> &v) const {
        for (auto &x : v) --x;
        return *this;
    }
} Dec;

struct sort_impl {
    template <class T>
    const sort_impl &operator>>(std::vector<T> &v) const {
        std::sort(v.begin(), v.end());
        return *this;
    }
} Sort;

struct unique_impl {
    template <class T>
    const unique_impl &operator>>(std::vector<T> &v) const {
        std::sort(v.begin(), v.end());
        v.erase(std::unique(v.begin(), v.end()), v.end());
        return *this;
    }
} Uniq;
#line 2 "/home/kuhaku/home/github/algo/lib/tree/undo_union_find.hpp"

/**
 * @brief Undo可能Union-Find
 * @details Implement (union by size)
 *
 * @see https://ei1333.github.io/luzhiled/snippets/structure/union-find.html
 */
struct undo_union_find {
    undo_union_find() : data(), history() {}
    undo_union_find(int _n) : data(_n, -1), history() {}

    int root(int x) { return this->data[x] < 0 ? x : this->root(this->data[x]); }
    int get_root(int k) { return this->root(k); }

    bool is_root(int k) const { return this->data[k] < 0; }

    bool same(int x, int y) { return this->root(x) == this->root(y); }
    bool is_same(int x, int y) { return this->same(x, y); }

    int size(int k) { return -this->data[this->root(k)]; }
    int get_size(int k) { return this->size(k); }

    bool unite(int x, int y) {
        x = this->root(x), y = this->root(y);
        this->history.emplace(x, this->data[x]);
        this->history.emplace(y, this->data[y]);
        if (x == y) return false;
        if (this->data[x] > this->data[y]) std::swap(x, y);
        this->data[x] += this->data[y];
        this->data[y] = x;
        return true;
    }

    void undo() {
        this->data[this->history.top().first] = this->history.top().second;
        this->history.pop();
        this->data[this->history.top().first] = this->history.top().second;
        this->history.pop();
    }

    int snapshot() const { return this->history.size(); }

    void rollback(int x = 0) {
        while ((int)(this->history.size()) > x) this->undo();
    }

  private:
    std::vector<int> data;
    std::stack<std::pair<int, int>> history;
};
#line 3 "/home/kuhaku/home/github/algo/lib/tree/union_find.hpp"

/**
 * @brief 素集合データ構造
 * @details Implement (union by size) + (path compression)
 * @see https://github.com/atcoder/ac-library/blob/master/atcoder/dsu.hpp
 */
struct union_find {
    union_find() = default;
    explicit union_find(int _n) : data(_n, -1) {}

    int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
    int get_root(int x) { return root(x); }

    bool is_root(int x) const { return data[x] < 0; }

    bool same(int x, int y) { return root(x) == root(y); }
    bool is_same(int x, int y) { return same(x, y); }

    int size(int x) { return -(data[root(x)]); }
    int get_size(int x) { return size(x); }

    std::vector<int> leaders() {
        std::vector<int> res;
        for (int i = 0; i < (int)data.size(); ++i) {
            if (is_root(i)) res.emplace_back(i);
        }
        return res;
    }

    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (data[x] > data[y]) std::swap(x, y);
        data[x] += data[y];
        data[y] = x;
        return true;
    }

    template <class F>
    bool unite(int x, int y, F f) {
        x = root(x), y = root(y);
        if (x != y) {
            if (data[x] > data[y]) std::swap(x, y);
            data[x] += data[y];
            data[y] = x;
        }
        f(x, y);
        return x != y;
    }

  private:
    std::vector<int> data;
};
#line 6 "a.cpp"

int main(void) {
    int n, m, k;
    cin >> n >> m >> k;
    vector<pair<int, int>> edges(m);
    cin >> edges;
    for (auto &[x, y] : edges) --x, --y;
    vector<int> s(n);
    cin >> s;
    Dec >> s;
    undo_union_find uf(n);
    map<pair<int, int>, vector<int>> mp;
    rep (i, m) {
        auto [x, y] = edges[i];
        if (s[x] == s[y]) {
            uf.unite(x, y);
        } else {
            if (s[x] > s[y])
                swap(x, y);
            mp[{s[x], s[y]}].emplace_back(i);
        }
    }

    map<pair<int, int>, vector<int>> pm;
    int q;
    cin >> q;
    vector<pair<int, int>> queries(q);
    cin >> queries;
    for (auto &[x, y] : queries) --x, --y;
    vector<int> ans(q, -1);
    rep (i, q) {
        auto [x, y] = queries[i];
        if (s[x] == s[y]) {
            ans[i] = uf.same(x, y);
        } else {
            if (s[x] > s[y])
                swap(x, y);
            pm[{s[x], s[y]}].emplace_back(i);
        }
    }

    int ss = uf.snapshot();
    for (auto &[p, v] : pm) {
        for (auto e : mp[p]) {
            uf.unite(edges[e].first, edges[e].second);
        }
        for (auto e : v) {
            ans[e] = uf.same(queries[e].first, queries[e].second);
        }
        uf.rollback(ss);
    }

    rep (i, q) co(ans[i]);

    return 0;
}
accuracy: 1
time_ms: 660
memory_kb: 132876
final_rank: 4

code_snippet: #include<bits/stdc++.h>
#define I first
#define II second
#define ii pair<int,int>
#define lg2(n) (31 - __builtin_clz(n))
#define ll long long
using namespace std;
const long long P = 1e9+7;
const int N = 4e5+2;
const long long INF = 1e18;

void input()
{
#define TASKNAME ""
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    if (fopen(TASKNAME".inp","r" ))
    {
        freopen(TASKNAME".inp","r",stdin);
        freopen(TASKNAME".out","w",stdout);
    }
}
int n, m, k;
typedef array<int, 4> it;
int p[N], sz[N],cnt, cur;

struct DSU
{
    vector<int> par, sz;
    vector<array<int, 4> > rb;
    void rz(int n)
    {
        par.assign(n + 1, 0);
        sz.assign(n + 1, 1);
        rb.clear();
        cur = 1;
    }
    int get(int u)
    {
        return (!par[u] ? u : get(par[u]));
    }
    int join(int u, int v)
    {
        u = get(u), v = get(v);
        if (u == v) return 0;
        if (sz[u] < sz[v]) swap(u, v);
        rb.push_back({u, sz[u], v, cur});
        sz[u] += sz[v];
        par[v] = u;
        cur = max(cur, sz[u]);
        return 1;
    }
    void rollback(int cnt)
    {
        while (cnt--)
        {
            auto G = rb.back(); rb.pop_back();
            sz[G[0]] = G[1];
            par[G[2]] = 0;
            cur = G[3];
        }
    }
} T;
it e[N];
int s[N], ans[N];
int main()
{
    input();
      cin >> n >> m >> k;
    T.rz(n);
    for (int i = 1; i <= m; i++) cin >> e[i][2] >> e[i][3];
    for (int i = 1; i <= n; i++) cin >> s[i];
    for (int i = 1; i <= m; i++)
    {
        if (s[e[i][2]] > s[e[i][3]]) swap(e[i][2], e[i][3]);
        e[i][0] = s[e[i][2]];
        e[i][1] = s[e[i][3]];
        if (e[i][0] == e[i][1]) T.join(e[i][2], e[i][3]);
    }
    sort(e + 1, e + m + 1);
    int q; cin >> q;
    vector<array<int, 5> > b;
    for (int i = 1; i <= q; i++)
    {
        int x, y;
        cin >> x >> y;
        if (s[x] > s[y]) swap(x, y);
        b.push_back({s[x], s[y], x, y, i});
    }
    sort(b.begin(), b.end());
    for (int i = 0; i < q; i++)
    {
        if (b[i][0] == b[i][1])
        {
            ans[b[i][4]] = (T.get(b[i][2]) == T.get(b[i][3]));
            continue;
        }
        int j = i;
        while (j + 1 < q && b[j + 1][0] == b[i][0] && b[j + 1][1] == b[i][1]) j++;
        int l = lower_bound(e + 1, e + m + 1, array<int, 4>{b[i][0], b[i][1], 0, 0}) - e;
        if (l == m + 1)
        {
            i = j;
            continue;
        }
        int cnt = 0;
        for (int k = l; k <= m; k++)
            if (e[k][0] == b[i][0] && e[k][1] == b[i][1])
            cnt += T.join(e[k][2], e[k][3]); else break;
        for (int k = i; k <= j; k++)
            ans[b[k][4]] = (T.get(b[k][2]) == T.get(b[k][3]));
        T.rollback(cnt);
        i = j;
    }
    for (int i = 1; i <= q; i++) cout << ans[i] << '\n';
    return 0;
}
accuracy: 1
time_ms: 230
memory_kb: 40968
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define chmin(x,y) x = min(x,y)
#define chmax(x,y) x = max(x,y)
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001




#define SIZE 400005

struct Edge{
	Edge(int arg_to,int arg_ind){
		to = arg_to;
		ind = arg_ind;
	}
	Edge(){

		to = ind = 0;
	}
	bool operator<(const struct Edge& arg) const{

		return ind < arg.ind;
	}
	int to,ind;
};

int N,M,K;
int boss[SIZE],height[SIZE],num_member[SIZE];
int A[SIZE],B[SIZE],S[SIZE];
int IND = 0;
vector<int> TO[SIZE];
map<pair<int,int>,int> MAP;
map<int,int> HEAD[SIZE];
map<int,int> MAXI[SIZE];
vector<Edge> G[SIZE];
set<int> SET;
int NUM;


int get_boss(int id){
	if(boss[id] == id)return id;
	else{
		return boss[id] = get_boss(boss[id]);
	}
}

int isSame(int x,int y){
	return get_boss(x) == get_boss(y);
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	if(boss_x == boss_y)return;

	if(height[boss_x] > height[boss_y]){

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;

	}else if(height[boss_x] < height[boss_y]){

		num_member[boss_y] += num_member[boss_x];
		boss[boss_x] = boss_y;

	}else{ //height[boss_x] == height[boss_y]

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;
		height[x]++;
	}
}

void dfs(int ind,int node){

	int size = G[node].size();
	if(size == 0)return;

	int head;

	auto at = HEAD[node].find(ind);
	if(at == HEAD[node].end()){

		return;
	}

	head = at->second;

	for(int i = head; i < G[node].size(); i++){
		if(G[node][i].ind != ind)break;

		int next = G[node][i].to;

		if(SET.count(next) > 0)continue;

		SET.insert(next);
		chmax(NUM,next); //■訪問済みの中で最大のノード番号

		dfs(ind,next);
	}
}

int getIndex(int a,int b){
	if(a > b){

		swap(a,b);
	}

	auto at = MAP.find(make_pair(a,b));

	if(at == MAP.end()){

		MAP[make_pair(a,b)] = IND++;
	}

	return MAP[make_pair(a,b)];
}

int main(){

	scanf("%d %d %d",&N,&M,&K);

	for(int i = 0; i < N; i++){

		boss[i] = i;
	}

	for(int i = 0; i < M; i++){

		scanf("%d %d",&A[i],&B[i]);
		A[i]--;
		B[i]--;
	}

	for(int i = 0; i < N; i++){

		scanf("%d",&S[i]);
	}

	//■同じグループの頂点をまとめる
	for(int i = 0; i < M; i++){
		if(S[A[i]] == S[B[i]]){

			unite(A[i],B[i]);
		}
	}

	//エッジを集約
	for(int i = 0; i < M; i++){
		if(S[A[i]] != S[B[i]]){

			int a = get_boss(A[i]);
			int b = get_boss(B[i]);

			TO[a].push_back(b);
			TO[b].push_back(a);

			getIndex(S[A[i]],S[B[i]]); //■隣接する州の組合せに番号を振る

		}
	}

	for(int i = 0; i < N; i++){
		if(i != get_boss(i))continue;
		if(TO[i].size() == 0)continue;

		sort(TO[i].begin(),TO[i].end());
		TO[i].erase(unique(TO[i].begin(),TO[i].end()),TO[i].end());

		for(int k = 0; k < TO[i].size(); k++){

			int to = TO[i][k]; //隣接グループ
			int ind = getIndex(S[i],S[to]); //エッジのタイプ

			G[i].push_back(Edge(to,ind));
		}
		sort(G[i].begin(),G[i].end());

		set<int> work;
		for(int k = 0; k < G[i].size(); k++){

			int ind = G[i][k].ind;
			if(work.count(ind) == 0){

				HEAD[i][ind] = k;
				work.insert(ind);
			}
		}
	}

	int numQ;
	scanf("%d",&numQ);

	int from,to;
	for(int loop = 0; loop < numQ; loop++){

		scanf("%d %d",&from,&to);
		from--;
		to--;

		if(S[from] == S[to]){

			if(isSame(from,to)){

				printf("1\n");

			}else{

				printf("0\n");
			}
			continue;
		}

		from = get_boss(from);
		to = get_boss(to);

		if(from > to){

			swap(from,to);
		}
		int ind = getIndex(S[from],S[to]);

		auto at = MAXI[ind].find(from);
		auto bt = MAXI[ind].find(to);

		if(at != MAXI[ind].end() && bt != MAXI[ind].end()){

			if(at->second == bt->second){

				printf("1\n");
			}else{

				printf("0\n");
			}
			continue;

		}else if(at == MAXI[ind].end() && bt != MAXI[ind].end()){

			printf("0\n");
			continue;

		}else if(at != MAXI[ind].end() && bt == MAXI[ind].end()){

			printf("0\n");
			continue;

		}else{ //at == MAX[ind].end() && bt == MAXI[ind].end();

			SET.clear();
			NUM = from;
			dfs(ind,from);
		}

		if(SET.count(to) > 0){

			printf("1\n");

		}else{

			printf("0\n");
		}
		for(int n: SET){

			MAXI[ind][n] = NUM;
		}
	}

	return 0;
}
accuracy: 1
time_ms: 1420
memory_kb: 181512
final_rank: 11

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
#define chmin(x,y) x = min(x,y)
#define chmax(x,y) x = max(x,y)
#define BIG_NUM 2000000000
#define HUGE_NUM 4000000000000000000 //オーバーフローに注意
#define MOD 1000000007
#define EPS 0.000000001




#define SIZE 400005

struct Edge{
	Edge(int arg_to,int arg_ind){
		to = arg_to;
		ind = arg_ind;
	}
	Edge(){

		to = ind = 0;
	}
	bool operator<(const struct Edge& arg) const{

		return ind < arg.ind;
	}
	int to,ind;
};

int N,M,K;
int boss[SIZE],height[SIZE],num_member[SIZE];
int A[SIZE],B[SIZE],S[SIZE];
int IND = 0;
vector<int> TO[SIZE];
map<pair<int,int>,int> MAP;
map<int,int> HEAD[SIZE];
map<int,int> MAXI[SIZE];
vector<Edge> G[SIZE];
set<int> SET;
int NUM;


int get_boss(int id){
	if(boss[id] == id)return id;
	else{
		return boss[id] = get_boss(boss[id]);
	}
}

int isSame(int x,int y){
	return get_boss(x) == get_boss(y);
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	if(boss_x == boss_y)return;

	if(height[boss_x] > height[boss_y]){

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;

	}else if(height[boss_x] < height[boss_y]){

		num_member[boss_y] += num_member[boss_x];
		boss[boss_x] = boss_y;

	}else{ //height[boss_x] == height[boss_y]

		num_member[boss_x] += num_member[boss_y];
		boss[boss_y] = boss_x;
		height[x]++;
	}
}

void dfs(int ind,int node){

	int size = G[node].size();
	if(size == 0)return;

	//printf("ind:%d node:%d\n",ind,node);

	int head;

	auto at = HEAD[node].find(ind);
	if(at == HEAD[node].end()){

		return;
	}

	head = at->second;

	for(int i = head; i < G[node].size(); i++){
		if(G[node][i].ind != ind)break;

		int next = G[node][i].to;

		if(SET.count(next) > 0)continue;

		SET.insert(next);
		chmax(NUM,next); //■訪問済みの中で最大のノード番号

		dfs(ind,next);
	}
}

int getIndex(int a,int b){
	if(a > b){

		swap(a,b);
	}

	auto at = MAP.find(make_pair(a,b));

	if(at == MAP.end()){

		MAP[make_pair(a,b)] = IND++;
	}

	return MAP[make_pair(a,b)];
}

int main(){

	scanf("%d %d %d",&N,&M,&K);

	for(int i = 0; i < N; i++){

		boss[i] = i;
	}

	for(int i = 0; i < M; i++){

		scanf("%d %d",&A[i],&B[i]);
		A[i]--;
		B[i]--;
	}

	for(int i = 0; i < N; i++){

		scanf("%d",&S[i]);
	}

	//■同じグループの頂点をまとめる
	for(int i = 0; i < M; i++){
		if(S[A[i]] == S[B[i]]){

			unite(A[i],B[i]);
		}
	}

	//エッジを集約
	for(int i = 0; i < M; i++){
		if(S[A[i]] != S[B[i]]){

			int a = get_boss(A[i]);
			int b = get_boss(B[i]);

			TO[a].push_back(b);
			TO[b].push_back(a);

			getIndex(S[A[i]],S[B[i]]); //■隣接する州の組合せに番号を振る

		}
	}

	for(int i = 0; i < N; i++){
		if(i != get_boss(i))continue;
		if(TO[i].size() == 0)continue;

		sort(TO[i].begin(),TO[i].end());
		TO[i].erase(unique(TO[i].begin(),TO[i].end()),TO[i].end());

		for(int k = 0; k < TO[i].size(); k++){

			int to = TO[i][k]; //隣接グループ
			int ind = getIndex(S[i],S[to]); //エッジのタイプ

			G[i].push_back(Edge(to,ind));
		}
		sort(G[i].begin(),G[i].end());

		set<int> work;
		for(int k = 0; k < G[i].size(); k++){

			int ind = G[i][k].ind;
			if(work.count(ind) == 0){

				HEAD[i][ind] = k;
				work.insert(ind);
			}
		}
	}

	int numQ;
	scanf("%d",&numQ);

	int from,to;
	for(int loop = 0; loop < numQ; loop++){

		scanf("%d %d",&from,&to);
		from--;
		to--;

		if(S[from] == S[to]){

			if(isSame(from,to)){

				printf("1\n");

			}else{

				printf("0\n");
			}
			continue;
		}

		from = get_boss(from);
		to = get_boss(to);

		if(from > to){

			swap(from,to);
		}
		int ind = getIndex(S[from],S[to]);

		auto at = MAXI[ind].find(from);
		auto bt = MAXI[ind].find(to);

		if(at != MAXI[ind].end() && bt != MAXI[ind].end()){

			if(at->second == bt->second){

				printf("1\n");
			}else{

				printf("0\n");
			}
			continue;

		}else if(at == MAXI[ind].end() && bt != MAXI[ind].end()){

			printf("0\n");
			continue;

		}else if(at != MAXI[ind].end() && bt == MAXI[ind].end()){

			printf("0\n");
			continue;

		}else{ //at == MAX[ind].end() && bt == MAXI[ind].end();

			SET.clear();
			NUM = from;
			dfs(ind,from);
		}

		//printf("from;%d to;%d ind;%d\n",from,to,ind);
		//continue;


		if(SET.count(to) > 0){

			printf("1\n");

		}else{

			printf("0\n");
		}
		for(int n: SET){

			MAXI[ind][n] = NUM;
		}
	}

	return 0;
}
accuracy: 1
time_ms: 1400
memory_kb: 181716
final_rank: 10

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>


using namespace std;
vector<int> pos;
vector<int> prev_root;
vector<int> prev_rank;

typedef struct st
{
    int S1;
    int S2;
    int C1;
    int C2;
    st(int S1, int S2, int C1, int C2) {
        this->S1 = S1;
        this->S2 = S2;
        this->C1 = C1;
        this->C2 = C2;
    }
} st;

typedef struct queries {
    int num;
    int C1;
    int C2;
    int S1;
    int S2;
    queries(int num, int C1, int C2, int S1, int S2) {
        this->num = num;
        this->C1 = C1;
        this->C2 = C2;
        this->S1 = S1;
        this->S2 = S2;
    }
} queries;

bool fcomp(const st& a, const st& b) {
    if (a.S1 < b.S1) {
        return true;
    }
    else {
        if (a.S1 > b.S1) {
            return false;
        }
        else {
            return a.S2 < b.S2;
        }
    }
}

bool fcompQ(const queries& a, const queries& b) {
    if (a.S1 < b.S1) {
        return true;
    }
    else {
        if (a.S1 > b.S1) {
            return false;
        }
        else {
            return a.S2 < b.S2;
        }
    }
}

int getRoot(int a, int mp[],int ranks[],bool flag) {
    int next = mp[a];
    int rankA = ranks[a];

    if (next == a) {
        return a;
    }
    else {
        int root = getRoot(next, mp,ranks,flag);
        if (flag) {
            pos.push_back(a);
            prev_root.push_back(next);
            prev_rank.push_back(rankA);
        }
        mp[a] = root; 
        ranks[a] = 1;
        return root;
    }
}

int isSame(int a, int b, int mp[], int ranks[],bool flag) {
    if (getRoot(a, mp,ranks, flag) == getRoot(b, mp,ranks, flag)) {
        return true;
    }
    else {
        return false;
    }
}

int unite(int a, int b, int mp[], int ranks[], bool flag) {
    int rootA = getRoot(a, mp,ranks,flag);
    int rootB = getRoot(b, mp,ranks, flag);

    if (rootA == rootB) {
        return 0;
    }
    else {
        int rankA = ranks[rootA];
        int rankB = ranks[rootB];
        if (rankA < rankB) {
            if (flag) {
                pos.push_back(rootA);
                prev_root.push_back(rootA);
                prev_rank.push_back(rankA);
            }
            mp[rootA] = rootB;
            ranks[rootA] = rankB + 1;
        }
        else {
            if (flag) {
                pos.push_back(rootB);
                prev_root.push_back(rootB);
                prev_rank.push_back(rankB);
            }
            mp[rootB] = rootA;
            ranks[rootB] = rankA + 1;
        }
        return 0;
    }

}

int undo(int mp[], int ranks[]) {
    int pos0;
    int root0;
    for (int i = prev_root.size() - 1; i >= 0; i--) {
        pos0 = pos[i];
        root0 = prev_root[i];
        mp[pos0] = root0;
        ranks[pos0] = prev_rank[i];
    }
    pos.clear();
    prev_root.clear();
    prev_rank.clear();
    return 0;
}

int main(void) {
    // Your code here!
    int N, M, K;
    cin >> N >> M >> K;  //N cities   M roads   K states
    int mp[N + 1];
    int ranks[N + 1];

    clock_t start = clock();

    for (int i = 1; i < N + 1; i++) {
        mp[i] = i;
        ranks[i] = 1;
    }

    int U[M], V[M];
    for (int j = 0; j < M; j++) {
        cin >> U[j] >> V[j];
    }
    int S[N + 1];
    for (int i = 1; i < N + 1; i++) {
        cin >> S[i];
    }

    vector <st> roads;
    roads.reserve(2 * M);

    int U0, V0;
    for (int j = 0; j < M; j++) {
        U0 = U[j];
        V0 = V[j];
        if (S[U0] == S[V0]) {
            unite(U0, V0, mp, ranks, false);
        }
        else {
            roads.push_back(st(S[U0], S[V0], U0, V0));
            roads.push_back(st(S[V0], S[U0], V0, U0));
        }
    }

    sort(roads.begin(), roads.end(), fcomp);
    /*
    for(int i=0;i<roads.size();i++){
        cout << roads[i].S1 <<"," <<roads[i].S2 <<","<<roads[i].C1 <<"," <<roads[i].C2 <<endl;
    }
    */

    /*
    for(int m=1;m<N+1;m++){
        cout << m  << ":" << mp[m] <<"(" << S[m] << ")" <<endl;
    }
    */

    int Q, A, B;    // Q trades
    int S1, S2;
    
    cin >> Q;

    int Answers[Q];
    vector <queries> Ques;
    Ques.reserve(Q);

    for (int i = 0; i < Q; i++) {
        cin >> A >> B;
        S1 = S[A];
        S2 = S[B];
        if (S1 > S2) {
            swap(A, B);
            S1 = S[A];
            S2 = S[B];
        }
        Ques.push_back(queries(i,A,B,S1,S2));
    }

    sort(Ques.begin(), Ques.end(), fcompQ);
    int prevS1 = 0;
    int prevS2 = 0;
    int num;
    for (int i = 0; i < Q; i++) {
        A = Ques[i].C1;
        B = Ques[i].C2;
        S1 = S[A];
        S2 = S[B];
        num = Ques[i].num;

        if (prevS1 != S1 || prevS2 != S2) {
            undo(mp, ranks);
            auto it1 = lower_bound(roads.begin(), roads.end(), st(S1, S2, A, B), fcomp);
            for (auto it = it1; it1 != roads.end(); ++it) {
                if (it->S1 != S1 || it->S2 != S2) {
                    break;
                }
                unite(it->C1, it->C2, mp, ranks, true);
            }
        }
        prevS1 = S1;
        prevS2 = S2;
        if (isSame(A, B, mp,ranks, true)) {
            Answers[num] = 1;
        }
        else {
            Answers[num] = 0;
        }
    }
    for (int i = 0; i < Q; i++) {
        cout << Answers[i] << endl;
    }

    /*
    for (int i = 0; i < Q; i++) {
        cin >> A >> B;
        S1 = S[A];
        S2 = S[B];
        if (S1 > S2) {
            swap(A, B);
            S1 = S[A];
            S2 = S[B];
        }
        auto it1 = lower_bound(roads.begin(), roads.end(), st(S1, S2, A, B), fcomp);
        for (auto it = it1; it1 != roads.end(); ++it) {
            if (it->S1 != S1 || it->S2 != S2) {
                break;
            }
            unite(it->C1, it->C2, mp, ranks, true);
        }
        if (isSame(A, B, mp,true)) {
            cout << "1" << endl;
        }
        else {
            cout << "0" << endl;
        }
        undo(mp, ranks);
    }
    */
}
accuracy: 1
time_ms: 730
memory_kb: 54732
final_rank: 3

code_snippet: #include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
map<int, int> mp; 
map<int ,int> ranks;
typedef struct st
{
    int S1;
    int S2;
    int C1;
    int C2;
    st(int S1,int S2,int C1,int C2){
        this->S1=S1;
        this->S2=S2;
        this->C1=C1;
        this->C2=C2;
    }
} st;

int getRoot(int a){
    if (mp[a]==a){
        return a;
    }else{
        int root=getRoot(mp[a]);
        mp[a]=root;
        return root;
    }
}

int isSame(int a,int b){
    if(getRoot(a)==getRoot(b)){
        return true;
    }else{
        return false;
    }
}

int unite(int a,int b){
    int rootA=getRoot(a);
    int rootB=getRoot(b);
    
    if (rootA==rootB) {
        return 0;
    }else{
        int rankA=ranks[rootA];
        int rankB=ranks[rootB];
        if(rankA<rankB){
            mp[rootA]=rootB;
            ranks[rootA]=rankB+1;
        }else{
            mp[rootB]=rootA;
            ranks[rootB]=rankA+1;
        }
        return 0;
    }
}

bool fcomp(const st& a, const st& b) {
    if(a.S1<b.S1){
        return true;
    }else{
        if(a.S1>b.S1){
            return false;
        }else{
            return a.S2<b.S2;
        }
    }
}


int main(void){
    // Your code here!
    int N,M,K;
    cin >> N >> M >> K;  //N cities   M roads   K states
    
    for(int i=1;i<N+1;i++){
        mp[i]=i;
        ranks[i]=1;
    }
    
    int U[M],V[M];
    for(int j=0;j<M;j++){
        cin >> U[j] >> V[j] ;
    }
    int S[N+1];
    for(int i=1;i<N+1;i++){
        cin >> S[i];
    }
    
    vector <st> roads;
    roads.reserve(2*M);
    
    int U0,V0;
    for(int j=0;j<M;j++){
        U0=U[j];
        V0=V[j];
        if(S[U0]==S[V0]){
            unite(U0,V0);
        }else{
            roads.push_back(st(S[U0],S[V0],U0,V0));
            roads.push_back(st(S[V0],S[U0],V0,U0));
        }
    }
    
    sort(roads.begin(),roads.end(),fcomp);
    
    /*
    for(int i=0;i<roads.size();i++){
        cout << roads[i].S1 <<"," <<roads[i].S2 <<","<<roads[i].C1 <<"," <<roads[i].C2 <<endl;
    }
    */
    
    /*
    for(int m=1;m<N+1;m++){
        cout << m  << ":" << mp[m] <<"(" << S[m] << ")" <<endl;
    }
    */
    
    int Q,A,B;    // Q trades
    int S1,S2;
    cin >> Q;
    for(int i=0;i<Q;i++){
        map<int, int> mp_backup(mp); 
        cin >> A >> B;
        S1=S[A];
        S2=S[B];
        if(S1>S2){
            swap(A,B);
            S1=S[A];
            S2=S[B];
        }
        
        auto it1=lower_bound(roads.begin(), roads.end(),st(S1,S2,A,B),fcomp); 
        for(auto it=it1;it1!=roads.end();++it){
            if (it->S1!=S1 || it->S2!=S2){
                break;
            }
            unite(A,B);
        }    
        
        /*
        for(int j=0;j<M;j++){
            if ( (S[U[j]]==S1 && S[V[j]]==S2) || (S[U[j]]==S2 && S[V[j]]==S1)){
                unite(U[j],V[j]);
            }
        }
        */
        /*
        for(int m=1;m<N+1;m++){
            cout << m  << ":" << mp[m] <<"(" << S[m] << ")" <<endl;
        }
        */
        
        if (isSame(A,B) ){
            cout <<"1"<< endl;    
        }else{
            cout <<"0"<< endl;
        }
        
        for(int m=1;m<N+1;m++){
            mp[m]=mp_backup[m];
        }       
    }
}
accuracy: 0.06756756756756757
time_ms: 90
memory_kb: 3476
final_rank: 12

code_snippet: #include <bits/extc++.h>

using namespace std;

#define rep(i,n) for(int i=0;i<(n);++i)
#define reps(i,n) for(int i=1;i<=(n);++i)
#define all(x) begin(x),end(x)
#define Fixed fixed << setprecision(12)
#define updiv(a, b) (((a) + (b) - 1) / (b))
using pii = pair<int,int>;
constexpr int32_t INF  = 0x3f3f3f3f;
constexpr int_fast64_t LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr int mod1 = 1e9+7;
constexpr int mod2 = 998244353;

template <class Func>
class FixPoint : Func {
public:
    explicit constexpr FixPoint(Func&& f) noexcept : Func(forward<Func>(f)) {}

    template <class... Args>
    constexpr decltype(auto) operator()(Args&&... args) const {
        return Func::operator()(*this, std::forward<Args>(args)...);
    }
};

template <class Func>
static inline constexpr decltype(auto) makeFixPoint(Func&& f) noexcept {
    return FixPoint<Func>{forward<Func>(f)};
}

template<class T, class U> istream& operator>>(istream& is, pair<T, U>& p){ is >> p.first >> p.second; return (is); }
template<class T, class U> ostream& operator<<(ostream& os, pair<T, U>& p){ os << p.first << ' ' << p.second; return (os); }
template<class T> istream& operator>>(istream& is, vector<T>& v){ for (auto &&e : v) is >> e; return (is); }
template<class T> ostream& operator<<(ostream& os, vector<T>& v){ for (auto &&e : v) os << e << ' '; return (os); }

template <class A, class B> inline bool chmax(A &a, const B &b) { return b > a && (a = b, true); }
template <class A, class B> inline bool chmin(A &a, const B &b) { return b < a && (a = b, true); }

template <class T> using max_heap = priority_queue<T>;
template <class T> using min_heap = priority_queue<T, vector<T>, greater<T> >;
template <class A, class B> using umap = unordered_map<A, B>;
template <class T> using uset = unordered_set<T>;

template <class T> using Max_Heap = __gnu_pbds::priority_queue<T, less<T>, __gnu_pbds::rc_binomial_heap_tag>;
template <class T> using Min_Heap = __gnu_pbds::priority_queue<T, greater<T>, __gnu_pbds::rc_binomial_heap_tag>;
template <class T> using Set = __gnu_pbds::tree<T, __gnu_pbds::null_type, less<T>, __gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update>;
template <class K, class V> using Umap = __gnu_pbds::gp_hash_table<K, V>;
template <class T> using Rope = __gnu_cxx::rope<T>;
using Trie = __gnu_pbds::trie<string, __gnu_pbds::null_type, __gnu_pbds::trie_string_access_traits<>, __gnu_pbds::pat_trie_tag, __gnu_pbds::trie_prefix_search_node_update>;

template <class T> void bye(T x){ cout << x << '\n'; exit(0); }
inline int square(int a){ return (a * a); }

constexpr int dx[] = {1,0,-1,0,1,1,-1,-1};
constexpr int dy[] = {0,-1,0,1,1,-1,-1,1};

struct UnionFindUndo {
    vector<int> data;
    vector<pair<int, int> > history;

    UnionFindUndo(int sz) {
        data.assign(sz, -1);
    }

    bool unite(int x, int y) {
        x = find(x), y = find(y);
        history.emplace_back(x, data[x]);
        history.emplace_back(y, data[y]);
        if(x == y) return (false);
        if(data[x] > data[y]) swap(x, y);
        data[x] += data[y];
        data[y] = x;
        return (true);
    }

    int find(int k) {
        if(data[k] < 0) return (k);
        return (find(data[k]));
    }

    int size(int k) {
        return (-data[find(k)]);
    }

    void undo() {
        data[history.back().first] = history.back().second;
        history.pop_back();
        data[history.back().first] = history.back().second;
        history.pop_back();
    }

    void snapshot() {
        while(history.size()) history.pop_back();
    }

    void rollback() {
        while(history.size()) undo();
    }
};


signed main(){
    cin.tie(nullptr);
    ios_base::sync_with_stdio(false);
    cout.setf(ios_base::fixed);
    cout.precision(12);
    
    int n, m, k;
    cin >> n >> m >> k;

    vector<int> u(m), v(m);
    vector<vector<int> > g(n);
    for (int i = 0; i < m; ++i) {
        cin >> u[i] >> v[i];
        --u[i], --v[i];

        g[u[i]].emplace_back(v[i]);
        g[v[i]].emplace_back(u[i]);
    }

    vector<int> s(n);

    cin >> s;

    for (auto &&e : s) --e;

    vector<int> group(n, -1);
    int cnt = 0;

    for (int i = 0; i < n; ++i) {
        if (group[i] == -1) {
            queue<int> que;
            que.emplace(i);
            group[i] = cnt;
            while (!que.empty()) {
                int cur = que.front();
                que.pop();
                for (auto to : g[cur]) {
                    if (s[to] == s[i] && group[to] == -1) {
                        que.emplace(to);
                        group[to] = cnt;
                    }
                }
            }
            cnt++;
        }
    }

    vector<umap<int, vector<pair<int, int> > > > edges(k);

    for (int i = 0; i < m; ++i) {
        auto [su, sv] = minmax({s[u[i]], s[v[i]]});
        edges[su][sv].emplace_back(u[i], v[i]);
    }

    int q;
    cin >> q;

    vector<umap<int, vector<tuple<int, int, int> > > > queries(k);

    for (int i = 0; i < q; ++i) {
        int a, b;
        cin >> a >> b;
        --a, --b;

        auto[sa, sb] = minmax({s[a], s[b]});

        queries[sa][sb].emplace_back(a, b, i);
    }

    vector<int> ans(q);
    UnionFindUndo uf(cnt);

    for (int i = 0; i < k; ++i) {
        for (auto [j, v] : queries[i]) {
            for (auto [a, b] : edges[i][j]) {
                uf.unite(group[a], group[b]);
            }
            for (auto [a, b, idx] : v) {
                if (uf.find(group[a]) == uf.find(group[b])) {
                    ans[idx] = 1;
                } else {
                    ans[idx] = 0;
                }
            }
            uf.rollback();
        }
    }

    for (auto e : ans) {
        cout << e << '\n';
    }
    
    return (0);
}
accuracy: 1
time_ms: 580
memory_kb: 183712
final_rank: 9