Problem:
Exponential Path
問題
$N$ 頂点の木があります。この木は $N-1$ 本の辺を持ち、 $i$ ($1 \leq i \leq N-1$) 本目の辺は頂点 $a_i$ と頂点 $b_i$ を結びます。
この木上の長さ $d$ の単純パスの
スコア
を、$2^d$ と定めます。
$Q$ 個の質問が与えられます。各質問は $2$ つの整数 $x, y$ ($1 \leq x < y \leq N$) からなります。各質問について、$2$ つの頂点 $x, y$ をこの順に通る単純パスすべてのスコアの総和を $998244353$ で割ったあまりを出力してください。
制約
$2 \leq N \leq 100000$
$1 \leq a_i < b_i \leq N$ ($1\leq i \leq N - 1$)
与えられるグラフは木である
$1 \leq Q \leq 50000$
$1 \leq x_i < y_i \leq N$ ($1\leq i \leq Q$)
入力はすべて整数
入力
入力は以下の形式で標準入力から与えられます。
$N$
$a_1$ $b_1$
$a_2$ $b_2$
$\vdots$
$a_{N-1}$ $b_{N-1}$
$Q$
$x_1$ $y_1$
$x_2$ $y_2$
$\vdots$
$x_Q$ $y_Q$
出力
$Q$ 行出力してください。$i$ 行目には、$i$ 個目の質問に対する答えを出力してください。
入力例 1
5
1 2
2 3
2 4
4 5
3
1 3
2 3
3 4
出力例 1
4
18
12
頂点 $1, 3$ をこの順に通る単純パスは $1 \to 2 \to 3$ のみで、このスコアは $2^2=4$ です。したがって $4$ を出力します。
頂点 $2, 3$ をこの順に通る単純パスは $1 \to 2 \to 3$ , $2 \to 3$ , $4 \to 2 \to 3$ , $5 \to 4 \to 2 \to 3$ の $4$ つであり、スコアはそれぞれ $2^2=4$ , $2^1=2$ , $2^2=4$ , $2^3=8$ であるので、$4+2+4+8=18$ を出力します。
頂点 $3, 4$ をこの順に通る単純パスは $3 \to 2 \to 4$ , $3 \to 2 \to 4 \to 5$ の $2$ つであり、スコアはそれぞれ $2^2=4$ , $2^3=8$ であるので、$4+8=12$ を出力します。

Submitted solutions:

code_snippet: // AOJ #3273 Exponential Path
// 2025.2.23

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 998244353;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0, c = gc();
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) { // 整数の表示（出力）
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}


int N;
vector<vector<int>> adj;
vector<int> parent;
vector<int> depth;

int LOGN;
vector<vector<int>> tbl;
vector<ll> dp;
vector<ll> tot;
vector<ll> pow2;

void dfs_build(int v, int p, int d) {
    parent[v] = p;
    depth[v] = d;
    for (int w : adj[v]) {
        if(w == p) continue;
        dfs_build(w, v, d+1);
    }
}

void dfs_dp(int v, int p) {
    dp[v] = 1;
    for (int w : adj[v]) {
        if(w == p) continue;
        dfs_dp(w, v);
        dp[v] = (dp[v] + (2 * dp[w]) % MOD) % MOD;
    }
}

void dfs_tot(int v, int p) {
    for (int w : adj[v]) {
        if(w == p) continue;
        tot[w] = ( dp[w] + (2 * ((tot[v] - (2 * dp[w]) % MOD + MOD) % MOD)) % MOD ) % MOD;
        dfs_tot(w, v);
    }
}

void buildLCA() {
    LOGN = 32 - __builtin_clz(N);
    tbl.assign(N+1, vector<int>(LOGN, -1));
    for (int i=1; i<=N; i++) tbl[i][0] = parent[i];
    for (int j=1; j<LOGN; j++){
        for (int i=1; i<=N; i++){
            int par = tbl[i][j-1];
            if(par == -1) tbl[i][j] = -1;
            else tbl[i][j] = tbl[par][j-1];
        }
    }
}

int LCA(int a, int b){
    if(depth[a] < depth[b]) swap(a,b);
    int d = depth[a]-depth[b];
    for (int i=0; i<LOGN; i++){
        if(d & (1<<i)) a = tbl[a][i];
    }
    if(a == b) return a;
    for (int i=LOGN-1; i>=0; i--){
        if(tbl[a][i] != tbl[b][i]){
            a = tbl[a][i];
            b = tbl[b][i];
        }
    }
    return parent[a];
}

int getKthAncestor(int v, int k){
    for (int i=0; i<LOGN; i++){
        if(k & (1<<i)){
            v = tbl[v][i];
            if(v == -1) break;
        }
    }
    return v;
}

int main(){
    N = Cin();
    adj.assign(N+1, vector<int>());
    parent.assign(N+1, -1);
    depth.assign(N+1, 0);
    dp.assign(N+1, 0);
    tot.assign(N+1, 0);

    for (int i=1; i<=N-1; i++){
        int a = Cin(), b = Cin();
        adj[a].push_back(b);
        adj[b].push_back(a);
    }

    dfs_build(1, -1, 0);

    dfs_dp(1, -1);
    tot[1] = dp[1];
    dfs_tot(1, -1);

    pow2.resize(N+1);
    pow2[0] = 1;
    for (int i=1; i<=N; i++) pow2[i] = (pow2[i-1] << 1) % MOD;

    buildLCA();

    int Q = Cin();
    while(Q--){
        int x = Cin(), y = Cin();
        int l = LCA(x,y);
        int d = depth[x] + depth[y] - 2 * depth[l];
        ll f = pow2[d];
        ll Bx;
        if(x == l){
            int child = getKthAncestor(y, d-1);
            Bx = ((tot[x] - (2 * dp[child]) % MOD) % MOD + MOD) % MOD;
        } else Bx = dp[x] % MOD;

        ll By = dp[y] % MOD;

        ll ans = (Bx * By) % MOD;
        ans = (ans * f) % MOD;
        Cout(ans);
    }
    return 0;
}
accuracy: 0.09090909090909091
time_ms: 30
memory_kb: 21944
final_rank: 13

code_snippet: #include "bits/stdc++.h"


using namespace std;

using i64 = long long;

constexpr i64 mod = 998244353;

int convert(string s){
    if(s.size() == 1)return s[0] - 'A';
    return (s[0] - 'A') * 26 + 26 + s[1] - 'A';
}

int main(){
    int n;
    cin >> n;
    vector<vector<int>> edges(n);
    for(int i = 0; i < n - 1; ++i){
        int a, b;
        cin >> a >> b;
        --a, --b;
        edges[a].emplace_back(b);
        edges[b].emplace_back(a);
    }
    vector<int> depth(n, 0);
    vector<vector<int>> child(n);
    function<void(int, int)> dfs = [&](int x, int pre){
        for(auto y : edges[x]){
            if(y != pre){
                child[x].emplace_back(y);
                depth[y] = depth[x] + 1;
                dfs(y, x);
            }
        }
    };
    dfs(0, -1);

    int q;
    cin >> q;
    vector<vector<int>> queries(n);
    vector<int> xs(q), ys(q);

    vector<i64> answers(q, -1);
    for(int i = 0; i < q; ++i){
        cin >> xs[i] >> ys[i];
        --xs[i], --ys[i];
    }

    /*
    auto inv = [&](i64 x){
        i64 po = mod - 2;
        i64 val = (po & 1 ? x : 1);
        i64 p = x;
        for(i64 i = 1; i < 60; ++i){
            if(po & (1LL << i)){
                val = val * p % mod;
            }
            p = (p * p) % mod;
        }
        return val;
    };
    cout << inv(2) << endl;
     */

    vector<i64> H(n);
    function<i64(int)> f = [&](int x){
        i64 h = 1;
        for(auto y : child[x]){
            h = (h + f(y) * 2) % mod;
        }
        return H[x] = h;
    };
    f(0);
    vector<i64> Hsum(n);
    vector<i64> G(n);
    function<void(int,i64)> g = [&](int x, i64 h){
        G[x] = h; // (2 * h + 1) % mod;
        i64 prod = h;
        for(auto y : child[x])prod = (prod + H[y] % mod);
        Hsum[x] = prod;
        for(auto y : child[x]){
            g(y, (2 * (prod - H[y]) + 1) % mod);
        }
    };
    g(0, 0);


    for(int i = 0; i < q; ++i){
        int x = xs[i], y = ys[i];
        if(depth[x] >= depth[y]){
            queries[x].emplace_back(i);
        }
        else{
            queries[y].emplace_back(i);
        }
    }
    vector<i64> tab(n + 1, 1);
    for(int i = 0; i < n; ++i){
        tab[i + 1] = tab[i] * 2 % mod;
    }

    vector<vector<int>> parent(n, vector<int>(20, -1));
    for(int i = 0; i < n; ++i){
        for(auto j : child[i]){
            parent[j][0] = i;
        }
    }
    for(int j = 0; j < 19; ++j){
        for(int i = 0; i < n; ++i){
            parent[i][j + 1] = parent[i][j] == -1 ? -1 : parent[parent[i][j]][j];
        }
    }
    auto kthparent = [&](int u, int k){
        for(int i = 0; i < 20; ++i){
            if(k & (1 << i)){
                u = parent[u][i];
                if(u == -1)return u;
            }
        }
        return u;
    };
    auto lca = [&](int u, int v){
        // log^2 n
        int ok = 0, ng = min(depth[u], depth[v]) + 1;
        while(ng - ok > 1){
            int dep = (ok + ng) / 2;
            if(kthparent(u, depth[u] - dep) == kthparent(v, depth[v] - dep)){
                ok = dep;
            }
            else{
                ng = dep;
            }
        }
        return ok;
    };


    unordered_map<int,int> ma;
    function<void(int)> fn = [&](int x){
        for(auto idx : queries[x]){
            if(answers[idx] != -1){
                continue;
            }
            int y = xs[idx] != x ? xs[idx] : ys[idx];
            if(ma.find(y) != ma.end()){
                i64 yside = G[ma[y]]; // Hsum[ma[y]] - H[y];
                // answers[idx] = (H[x] * (G[y] + yside)) % mod;
                answers[idx] = (H[x] * yside) % mod;
                answers[idx] = answers[idx] * tab[depth[x] - depth[y]] % mod;
            }
            else{
                // TODO
                answers[idx] = (H[x] * H[y] % mod);
                answers[idx] = answers[idx] * tab[depth[x] + depth[y] - 2 * lca(x, y)] % mod;
            }
        }
        for(auto y : child[x]){
            ma[x] = y;
            fn(y);
            ma.erase(x);
        }
    };
    fn(0);
    for(int i = 0; i < q; ++i){
        cout << answers[i] << endl;
    }
}
accuracy: 1
time_ms: 190
memory_kb: 50684
final_rank: 12

code_snippet: #line 1 "main.cpp"
#pragma region Macros
#include <bits/stdc++.h>
using namespace std;
template <class T> inline bool chmax(T &a, T b) {
    if(a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <class T> inline bool chmin(T &a, T b) {
    if(a > b) {
        a = b;
        return 1;
    }
    return 0;
}
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

struct Setup {
    Setup() {
        cin.tie(0);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(15);
    }
} __Setup;

using ll = long long;
#define OVERLOAD3(_1, _2, _3, name, ...) name
#define ALL(v) (v).begin(), (v).end()
#define RALL(v) (v).rbegin(), (v).rend()
#define REP1(i, n) for(int i = 0; i < int(n); i++)
#define REP2(i, a, b) for(int i = (a); i < int(b); i++)
#define REP(...) OVERLOAD3(__VA_ARGS__, REP2, REP1)(__VA_ARGS__)
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) ((int)(v).size())
const int INF = 1 << 30;
const ll LLINF = 1LL << 60;
constexpr int MOD = 1000000007;
constexpr int MOD2 = 998244353;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};

void Case(int i) { cout << "Case #" << i << ": "; }
int popcount(int x) { return __builtin_popcount(x); }
ll popcount(ll x) { return __builtin_popcountll(x); }
#pragma endregion Macros

#line 1 "/Users/siro53/kyo-pro/compro_library/math/modint.hpp"
template <int mod> struct ModInt {
    int x;
    ModInt() : x(0) {}
    ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
    ModInt &operator+=(const ModInt &p) {
        if((x += p.x) >= mod)
            x -= mod;
        return *this;
    }
    ModInt &operator-=(const ModInt &p) {
        if((x += mod - p.x) >= mod)
            x -= mod;
        return *this;
    }
    ModInt &operator*=(const ModInt &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }
    ModInt &operator/=(const ModInt &p) {
        *this *= p.inv();
        return *this;
    }
    ModInt operator-() const { return ModInt(-x); }
    ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }
    ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }
    ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }
    ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }
    bool operator==(const ModInt &p) const { return x == p.x; }
    bool operator!=(const ModInt &p) const { return x != p.x; }
    ModInt inv() const {
        int a = x, b = mod, u = 1, v = 0, t;
        while(b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return ModInt(u);
    }
    ModInt pow(int64_t n) const {
        ModInt ret(1), mul(x);
        while(n > 0) {
            if(n & 1)
                ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream &operator<<(ostream &os, const ModInt &p) {
        return os << p.x;
    }
    friend istream &operator>>(istream &is, ModInt &a) {
        int64_t t;
        is >> t;
        a = ModInt<mod>(t);
        return (is);
    }
    static int get_mod() { return mod; }
};
#line 77 "main.cpp"
using mint = ModInt<MOD2>;
#line 1 "/Users/siro53/kyo-pro/compro_library/graph/hld.hpp"
class HeavyLightDecomposition {
    int N;
    vector<vector<int>> G;
    vector<int> in, out, sz, head, par, dep, rev;
    bool isBuilt;

    void dfs_sz(int u, int p, int d) {
        dep[u] = d;
        if(!G[u].empty() and G[u].front() == p) swap(G[u].front(), G[u].back());
        for(int& v : G[u]) {
            if(v == p) continue;
            dfs_sz(v, u, d+1);
            sz[u] += sz[v];
            if(sz[v] > sz[G[u].front()]) swap(v, G[u].front());
        }
    }

    void dfs_hld(int u, int p, int &k) {
        par[u] = p;
        in[u] = k++;
        rev[in[u]] = u;
        for(const int& v : G[u]) {
            if(v == p) continue;
            head[v] = (v == G[u].front() ? head[u] : v);
            dfs_hld(v, u, k);
        }
        out[u] = k;
    }

  public:
    HeavyLightDecomposition(int N)
        : N(N), G(N), in(N, -1), out(N, -1), sz(N, 1), head(N), par(N, -1), dep(N), rev(N), isBuilt(false) {}

    HeavyLightDecomposition(const vector<vector<int>> &g)
        : N((int)g.size()), G(N), in(N, -1), out(N, -1), sz(N, 1), head(N),
          par(N, -1), dep(N), rev(N), isBuilt(false) {}

    inline void add_edge(int u, int v) {
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }

    void build(int root = 0) {
        dfs_sz(root, -1, 0);
        head[root] = root;
        int k = 0;
        dfs_hld(root, -1, k);
        isBuilt = true;
        // debug(in, head);
    }

    int operator[](int u) {
        assert(isBuilt and 0 <= u and u < N);
        return in[u];
    }

    int lca(int u, int v) {
        assert(isBuilt and 0 <= u and u < N and 0 <= v and v < N);
        while(true) {
            if(in[u] > in[v]) swap(u, v);
            if(head[u] == head[v]) return u;
            v = par[head[v]];
        }
    }

    int dist(int u, int v) {
        assert(isBuilt and 0 <= u and u < N and 0 <= v and v < N);
        return dep[u] + dep[v] - 2 * dep[lca(u, v)];
    }

    // 頂点uから親方向に距離k登った頂点を返す
    int la(int u, int k) {
        assert(isBuilt and 0 <= u and u < N);
        if(dep[u] < k) return -1; 
        while(true) {
            if(in[u] - k >= in[head[u]]) return rev[in[u] - k];
            k -= (in[u] - in[head[u]] + 1);
            u = par[head[u]];
        }
    }   

    // 頂点属性
    vector<pair<int, int>> path(int u, int v) {
        assert(isBuilt and 0 <= u and u < N and 0 <= v and v < N);
        // u -> vの向きになるように返す
        vector<pair<int, int>> ret;
        while(true) {
            if(in[u] > in[v]) swap(u, v);
            if(head[u] == head[v]) {
                ret.emplace_back(in[u], in[v] + 1);
                break;
            } else {
                ret.emplace_back(in[head[v]], in[v] + 1);
                v = par[head[v]];
            }
        }
        return ret;
    }

    // 辺属性
    vector<pair<int, int>> path_edge(int u, int v) {
        assert(isBuilt and 0 <= u and u < N and 0 <= v and v < N);
        vector<pair<int, int>> ret;
        while(true) {
            if(in[u] > in[v]) swap(u, v);
            if(head[u] == head[v]) {
                if(u != v) ret.emplace_back(in[u] + 1, in[v] + 1);
                break;
            } else {
                ret.emplace_back(in[head[v]], in[v] + 1);
                v = par[head[v]];
            }
        }
        return ret;
    }

    pair<int, int> subtree(int u) { 
        assert(isBuilt and 0 <= u and u < N);
        return {in[u], out[u]};
    }
};
#line 79 "main.cpp"

int main() {
    int N;
    cin >> N;
    vector<vector<int>> G(N);
    HeavyLightDecomposition hld(N);
    REP(_, N-1) {
        int a, b;
        cin >> a >> b;
        a--, b--;
        G[a].push_back(b);
        G[b].push_back(a);
        hld.add_edge(a, b);
    }
    hld.build();
    
    vector<mint> dp1(N), dp2(N);
    vector<int> dep(N);
    auto dfs1 = [&](auto&& self, int u, int p, int d) -> mint {
        mint res = 0;
        dep[u] = d;
        for(int v : G[u]) if(v != p) {
            mint tmp = self(self, v, u, d+1);
            res += tmp * 2 + 2;
        }
        return (dp1[u] = res);
    };
    auto dfs2 = [&](auto&& self, int u, int p) -> void {
        dp2[u] = dp1[u];
        for(int v : G[u]) if(v != p) {
            mint dpu = dp1[u];
            mint dpv = dp1[v];

            dp1[u] -= dp1[v] * 2 + 2;
            dp1[v] += dp1[u] * 2 + 2;
            self(self, v, u);

            dp1[u] = dpu;
            dp1[v] = dpv;
        }
    };
    dfs1(dfs1, 0, -1, 0);
    dfs2(dfs2, 0, -1);

    int Q;
    cin >> Q;
    REP(_, Q) {
        int x, y;
        cin >> x >> y;
        x--, y--;
        mint ans = mint(2).pow(hld.dist(x, y));
        int lca = hld.lca(x, y);
        debug(x, y, lca);
        {
            if(x == lca) {
                int q = hld.la(y, dep[y] - dep[x] - 1);
                debug(q);
                ans *= dp2[x] - dp1[q] * 2 - 2 + 1;
            } else {
                int q = hld.la(x, 1);
                debug(q);
                ans *= dp1[x] + 1;
            }
        }
        swap(x, y);
        {
            if(x == lca) {
                int q = hld.la(y, dep[y] - dep[x] - 1);
                ans *= dp2[x] - dp1[q] * 2 - 2 + 1;
            } else {
                ans *= dp1[x] + 1;
            }
        }
        cout << ans << endl;
    }
}
accuracy: 1
time_ms: 80
memory_kb: 27156
final_rank: 2

code_snippet: #line 1 "i.cpp"
#pragma region Macros
#include <bits/stdc++.h>
using namespace std;
template <class T> inline bool chmax(T &a, T b) {
    if(a < b) {
        a = b;
        return 1;
    }
    return 0;
}
template <class T> inline bool chmin(T &a, T b) {
    if(a > b) {
        a = b;
        return 1;
    }
    return 0;
}
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

struct Setup {
    Setup() {
        cin.tie(0);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(15);
    }
} __Setup;

using ll = long long;
#define OVERLOAD3(_1, _2, _3, name, ...) name
#define ALL(v) (v).begin(), (v).end()
#define RALL(v) (v).rbegin(), (v).rend()
#define REP1(i, n) for(int i = 0; i < int(n); i++)
#define REP2(i, a, b) for(int i = (a); i < int(b); i++)
#define REP(...) OVERLOAD3(__VA_ARGS__, REP2, REP1)(__VA_ARGS__)
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) ((int)(v).size())
const int INF = 1 << 30;
const ll LLINF = 1LL << 60;
constexpr int MOD = 1000000007;
constexpr int MOD2 = 998244353;
const int dx[4] = {1, 0, -1, 0};
const int dy[4] = {0, 1, 0, -1};

void Case(int i) { cout << "Case #" << i << ": "; }
int popcount(int x) { return __builtin_popcount(x); }
ll popcount(ll x) { return __builtin_popcountll(x); }
#pragma endregion Macros

#line 1 "/Users/siro53/kyo-pro/compro_library/math/modint.hpp"
template <int mod> struct ModInt {
    int x;
    ModInt() : x(0) {}
    ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
    ModInt &operator+=(const ModInt &p) {
        if((x += p.x) >= mod)
            x -= mod;
        return *this;
    }
    ModInt &operator-=(const ModInt &p) {
        if((x += mod - p.x) >= mod)
            x -= mod;
        return *this;
    }
    ModInt &operator*=(const ModInt &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }
    ModInt &operator/=(const ModInt &p) {
        *this *= p.inv();
        return *this;
    }
    ModInt operator-() const { return ModInt(-x); }
    ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }
    ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }
    ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }
    ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }
    bool operator==(const ModInt &p) const { return x == p.x; }
    bool operator!=(const ModInt &p) const { return x != p.x; }
    ModInt inv() const {
        int a = x, b = mod, u = 1, v = 0, t;
        while(b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return ModInt(u);
    }
    ModInt pow(int64_t n) const {
        ModInt ret(1), mul(x);
        while(n > 0) {
            if(n & 1)
                ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }
    friend ostream &operator<<(ostream &os, const ModInt &p) {
        return os << p.x;
    }
    friend istream &operator>>(istream &is, ModInt &a) {
        int64_t t;
        is >> t;
        a = ModInt<mod>(t);
        return (is);
    }
    static int get_mod() { return mod; }
};
#line 77 "i.cpp"
using mint = ModInt<MOD2>;
#line 1 "/Users/siro53/kyo-pro/compro_library/graph/lca.hpp"
struct LCA {
    const int n = 0;
    const int log2_n = 0;
    vector<vector<int>> par;
    vector<int> depth;

    LCA(const vector<vector<int>> &g, int root)
        : n(g.size()), log2_n(log2(n) + 1), par(log2_n, vector<int>(n)),
          depth(n) {
        dfs(g, root, -1, 0);
        for(int k = 0; k + 1 < log2_n; k++) {
            for(int v = 0; v < n; v++) {
                if(par[k][v] < 0) {
                    par[k + 1][v] = -1;
                } else {
                    par[k + 1][v] = par[k][par[k][v]];
                }
            }
        }
    }

    void dfs(const vector<vector<int>> &g, int v, int p, int d) {
        par[0][v] = p;
        depth[v] = d;
        for(auto &u : g[v]) {
            if(u != p) {
                dfs(g, u, v, d + 1);
            }
        }
    }

    int get_lca(int u, int v) {
        // uとvの深さが同じになるまで辿る
        if(depth[u] > depth[v]) {
            swap(u, v);
        }
        for(int k = 0; k < log2_n; k++) {
            if(((depth[v] - depth[u]) >> k) & 1) {
                v = par[k][v];
            }
        }

        if(u == v) {
            return u;
        }

        for(int k = log2_n - 1; k >= 0; k--) {
            if(par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }

    int get_dist(int u, int v) {
        return depth[u] + depth[v] - 2 * depth[get_lca(u, v)];
    }
};
#line 79 "i.cpp"

int N;
vector<vector<int>> G;
mint dp1[100010], dp2[100010], ans[100010];
int depth[100010];
vector<pair<int, int>> li[100010];

mint dfs1(int u, int p, int d) {
    mint res = 0;
    depth[u] = d;
    for(int v : G[u]) if(v != p) {
        mint tmp = dfs1(v, u, d + 1);
        res += tmp * 2 + 2;
    }
    return (dp1[u] = res);
}

void dfs2(int u, int p) {
    dp2[u] = dp1[u];
    for(auto [v, qid] : li[u]) ans[qid] *= (dp1[v] + 1);
    for(int v : G[u]) if(v != p) {
        mint dpu = dp1[u];
        mint dpv = dp1[v];

        dp1[u] -= dp1[v] * 2 + 2;
        dp1[v] += dp1[u] * 2 + 2;
        dfs2(v, u);

        dp1[u] = dpu;
        dp1[v] = dpv;
    }
}

int main() {
    cin >> N;
    G.resize(N);
    REP(_, N-1) {
        int a, b;
        cin >> a >> b;
        a--, b--;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    LCA lca(G, 0);
    int Q;
    cin >> Q;
    vector<pair<int, int>> qs(Q);
    REP(i, Q) {
        auto& [x, y] = qs[i];
        cin >> x >> y;
        x--, y--;
        ans[i] = mint(2).pow(lca.get_dist(x, y));
        li[x].emplace_back(y, i);
        li[y].emplace_back(x, i);
    }

    dfs1(0, -1, 0);
    dfs2(0, -1);

    // REP(i, N) debug(i, dp1[i], dp2[i], ans[i]);
    REP(i, Q) cout << ans[i] << endl;
}
accuracy: 1
time_ms: 80
memory_kb: 29116
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < n; i++)
#define rep2(i, x, n) for (int i = x; i <= n; i++)
#define rep3(i, x, n) for (int i = x; i >= n; i--)
#define each(e, v) for (auto &e : v)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;

template <typename T>
bool chmax(T &x, const T &y) {
    return (x < y) ? (x = y, true) : false;
}

template <typename T>
bool chmin(T &x, const T &y) {
    return (x > y) ? (x = y, true) : false;
}

template <typename T>
int flg(T x, int i) {
    return (x >> i) & 1;
}

template <typename T>
void print(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << (i == n - 1 ? '\n' : ' ');
    if (v.empty()) cout << '\n';
}

template <typename T>
void printn(const vector<T> &v, T x = 0) {
    int n = v.size();
    for (int i = 0; i < n; i++) cout << v[i] + x << '\n';
}

template <typename T>
int lb(const vector<T> &v, T x) {
    return lower_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
int ub(const vector<T> &v, T x) {
    return upper_bound(begin(v), end(v), x) - begin(v);
}

template <typename T>
void rearrange(vector<T> &v) {
    sort(begin(v), end(v));
    v.erase(unique(begin(v), end(v)), end(v));
}

template <typename T>
vector<int> id_sort(const vector<T> &v, bool greater = false) {
    int n = v.size();
    vector<int> ret(n);
    iota(begin(ret), end(ret), 0);
    sort(begin(ret), end(ret), [&](int i, int j) { return greater ? v[i] > v[j] : v[i] < v[j]; });
    return ret;
}

template <typename S, typename T>
pair<S, T> operator+(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first + q.first, p.second + q.second);
}

template <typename S, typename T>
pair<S, T> operator-(const pair<S, T> &p, const pair<S, T> &q) {
    return make_pair(p.first - q.first, p.second - q.second);
}

template <typename S, typename T>
istream &operator>>(istream &is, pair<S, T> &p) {
    S a;
    T b;
    is >> a >> b;
    p = make_pair(a, b);
    return is;
}

template <typename S, typename T>
ostream &operator<<(ostream &os, const pair<S, T> &p) {
    return os << p.first << ' ' << p.second;
}

struct io_setup {
    io_setup() {
        ios_base::sync_with_stdio(false);
        cin.tie(NULL);
        cout << fixed << setprecision(15);
    }
} io_setup;

const int inf = (1 << 30) - 1;
const ll INF = (1LL << 60) - 1;
// const int MOD = 1000000007;
const int MOD = 998244353;

template <int mod>
struct Mod_Int {
    int x;

    Mod_Int() : x(0) {}

    Mod_Int(long long y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

    static int get_mod() { return mod; }

    Mod_Int &operator+=(const Mod_Int &p) {
        if ((x += p.x) >= mod) x -= mod;
        return *this;
    }

    Mod_Int &operator-=(const Mod_Int &p) {
        if ((x += mod - p.x) >= mod) x -= mod;
        return *this;
    }

    Mod_Int &operator*=(const Mod_Int &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }

    Mod_Int &operator/=(const Mod_Int &p) {
        *this *= p.inverse();
        return *this;
    }

    Mod_Int &operator++() { return *this += Mod_Int(1); }

    Mod_Int operator++(int) {
        Mod_Int tmp = *this;
        ++*this;
        return tmp;
    }

    Mod_Int &operator--() { return *this -= Mod_Int(1); }

    Mod_Int operator--(int) {
        Mod_Int tmp = *this;
        --*this;
        return tmp;
    }

    Mod_Int operator-() const { return Mod_Int(-x); }

    Mod_Int operator+(const Mod_Int &p) const { return Mod_Int(*this) += p; }

    Mod_Int operator-(const Mod_Int &p) const { return Mod_Int(*this) -= p; }

    Mod_Int operator*(const Mod_Int &p) const { return Mod_Int(*this) *= p; }

    Mod_Int operator/(const Mod_Int &p) const { return Mod_Int(*this) /= p; }

    bool operator==(const Mod_Int &p) const { return x == p.x; }

    bool operator!=(const Mod_Int &p) const { return x != p.x; }

    Mod_Int inverse() const {
        assert(*this != Mod_Int(0));
        return pow(mod - 2);
    }

    Mod_Int pow(long long k) const {
        Mod_Int now = *this, ret = 1;
        for (; k > 0; k >>= 1, now *= now) {
            if (k & 1) ret *= now;
        }
        return ret;
    }

    friend ostream &operator<<(ostream &os, const Mod_Int &p) { return os << p.x; }

    friend istream &operator>>(istream &is, Mod_Int &p) {
        long long a;
        is >> a;
        p = Mod_Int<mod>(a);
        return is;
    }
};

using mint = Mod_Int<MOD>;

template <bool directed = false>
struct Lowest_Common_Ancestor {
    struct edge {
        int to, id;
        edge(int to, int id) : to(to), id(id) {}
    };

    vector<vector<edge>> es;
    vector<vector<int>> par; // par[i][j] := 頂点 j の 2^i 個前の祖先
    vector<int> depth;
    const int n;
    int height, m;

    Lowest_Common_Ancestor(int n) : es(n), depth(n), n(n), m(0) {
        height = 1;
        while ((1 << height) < n) height++;
        par.assign(height, vector<int>(n));
    }

    void add_edge(int from, int to) {
        es[from].emplace_back(to, m);
        if (!directed) es[to].emplace_back(from, m);
        m++;
    }

    void prepare(int now, int pre = -1) {
        if (pre == -1) depth[now] = 0;
        par[0][now] = pre;
        for (auto &e : es[now]) {
            if (e.to != pre) {
                depth[e.to] = depth[now] + 1;
                prepare(e.to, now);
            }
        }
    }

    void build(int root = 0) { // root を根として前準備する
        prepare(root);
        for (int j = 0; j < height - 1; j++) {
            for (int i = 0; i < n; i++) {
                if (par[j][i] == -1) {
                    par[j + 1][i] = -1;
                } else {
                    par[j + 1][i] = par[j][par[j][i]];
                }
            }
        }
    }

    int ancestor(int u, int k) { // u の k 個前の祖先
        for (int i = 0; i < height; i++) {
            if ((k >> i) & 1) u = par[i][u];
            if (u == -1) return -1;
        }
        return u;
    }

    int lca(int u, int v) {
        if (depth[u] < depth[v]) swap(u, v);
        u = ancestor(u, depth[u] - depth[v]);
        if (u == v) return u;
        for (int i = height - 1; i >= 0; i--) {
            if (par[i][u] != par[i][v]) u = par[i][u], v = par[i][v];
        }
        return par[0][u];
    }

    int dist(int u, int v) { return depth[u] + depth[v] - depth[lca(u, v)] * 2; }
};

template <typename sum_t, typename key_t, bool directed = false>
struct Rerooting {
    struct edge {
        int to;
        key_t data;
        sum_t dp, ndp; // to 側の部分木 dp（辺も含む）、from 側の部分木 dp（辺は含まない）
        edge(int to, key_t data, sum_t dp, sum_t ndp) : to(to), data(data), dp(dp), ndp(ndp) {}
    };

    using F = function<sum_t(sum_t, sum_t)>;
    using G = function<sum_t(sum_t, key_t)>;
    vector<vector<edge>> es;
    vector<sum_t> subdp, dp;  // 部分木の dp、全方位の dp
    const F f;                // 1 頂点を間に挟んで隣り合う 2 つの部分木の情報をマージ
    const G g;                // 部分木の根に 1 本辺を足す
    const sum_t e1;           // f の単位元
    const vector<sum_t> base; // 1 頂点の場合の dp の値

    Rerooting(int n, const F &f, const G &g, const sum_t &e1, const vector<sum_t> &base) : es(n), subdp(base), dp(n), f(f), g(g), e1(e1), base(base) {}

    void add_edge(int from, int to, const key_t &data) {
        es[from].emplace_back(to, data, e1, base[from]);
        if (!directed) es[to].emplace_back(from, data, e1, base[to]);
    }

    void dfs_sub(int now, int pre = -1) {
        for (auto &e : es[now]) {
            if (e.to == pre) continue;
            dfs_sub(e.to, now);
            subdp[now] = f(subdp[now], g(subdp[e.to], e.data));
        }
    }

    void dfs_all(int now, const sum_t &top, int pre = -1) {
        sum_t S = e1;
        for (int i = 0; i < (int)es[now].size(); i++) {
            auto &e = es[now][i];
            e.ndp = f(e.ndp, S);
            e.dp = g(e.to == pre ? top : subdp[e.to], e.data);
            S = f(S, e.dp);
        }
        dp[now] = f(base[now], S);
        S = e1;
        for (int i = (int)es[now].size() - 1; i >= 0; i--) {
            auto &e = es[now][i];
            e.ndp = f(e.ndp, S);
            if (e.to != pre) dfs_all(e.to, e.ndp, now);
            S = f(S, e.dp);
        }
    }

    vector<sum_t> solve() {
        dfs_sub(0);
        dfs_all(0, e1);
        return dp;
    }
};

int main() {
    int N;
    cin >> N;

    auto f = [](mint a, mint b) { return a + b; };
    auto g = [](mint a, mint b) { return a * b; };
    Rerooting<mint, mint> G1(N, f, g, 0, vector<mint>(N, 1));
    Lowest_Common_Ancestor G2(N);

    rep(i, N - 1) {
        int u, v;
        cin >> u >> v;
        u--, v--;
        G1.add_edge(u, v, 2), G2.add_edge(u, v);
    }
    G1.solve(), G2.build();

    int Q;
    cin >> Q;

    vector<mint> pw(N + 1, 1);
    rep(i, N) pw[i + 1] = pw[i] * 2;

    while (Q--) {
        int u, v;
        cin >> u >> v;
        u--, v--;

        int w = G2.lca(u, v), d = G2.dist(u, v);

        if (u != w && v != w) {
            cout << G1.subdp[u] * G1.subdp[v] * pw[d] << '\n';
        } else {
            if (u == w) swap(u, v);
            int c = G2.ancestor(u, d - 1);
            cout << G1.subdp[u] * (G1.dp[v] - G1.subdp[c] * 2) * pw[d] << '\n';
        }
    }
}
accuracy: 1
time_ms: 70
memory_kb: 37996
final_rank: 8

code_snippet: #include<iostream>
#include<vector>
using namespace std;
const int mod=998244353;
int N,Q;
vector<int>G[1<<17];
vector<pair<int,int> >qs[1<<17];
int ans[50505],vs[1<<17];
int depth[1<<17],parent[17][1<<17];
int dfs1(int u,int p,int d)
{
	depth[u]=d;
	parent[0][u]=p;
	vs[u]=1;
	for(int v:G[u])if(v!=p)(vs[u]+=dfs1(v,u,d+1)*2%mod)%=mod;
	return vs[u];
}
int lca(int u,int v)
{
	if(depth[u]>depth[v])swap(u,v);
	for(int k=0;k<17;k++)if(depth[v]-depth[u]>>k&1)v=parent[k][v];
	if(u==v)return u;
	for(int k=17;k--;)if(parent[k][u]!=parent[k][v])
	{
		u=parent[k][u];
		v=parent[k][v];
	}
	return parent[0][u];
}
int dist(int u,int v)
{
	int w=lca(u,v);
	return depth[u]+depth[v]-2*depth[w];
}
void dfs2(int u,int p)
{
	for(pair<int,int>q:qs[u])
	{
		ans[q.second]=(long)ans[q.second]*vs[q.first]%mod;
	}
	for(int v:G[u])if(v!=p)
	{
		int tu=vs[u],tv=vs[v];
		(vs[u]+=mod-vs[v]*2%mod)%=mod;
		(vs[v]+=vs[u]*2%mod)%=mod;
		dfs2(v,u);
		vs[u]=tu,vs[v]=tv;
	}
}
int pw[1<<17];
int main()
{
	cin>>N;
	for(int i=1;i<N;i++)
	{
		int u,v;cin>>u>>v;u--,v--;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs1(0,-1,0);
	for(int k=1;k<17;k++)
	{
		for(int i=0;i<N;i++)
		{
			if(parent[k-1][i]==-1)parent[k][i]=-1;
			else parent[k][i]=parent[k-1][parent[k-1][i]];
		}
	}
	pw[0]=1;
	for(int i=0;i<N;i++)pw[i+1]=pw[i]*2%mod;
	cin>>Q;
	for(int i=0;i<Q;i++)
	{
		int u,v;cin>>u>>v;u--,v--;
		qs[u].push_back(make_pair(v,i));
		qs[v].push_back(make_pair(u,i));
		ans[i]=pw[dist(u,v)];
	}
	dfs2(0,-1);
	for(int i=0;i<Q;i++)cout<<ans[i]<<"\n";
}
accuracy: 1
time_ms: 70
memory_kb: 30936
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;
const int LOG = 17;
const long long MOD = 998244353;
struct lowest_common_ancestor{
  vector<int> d;
  vector<vector<int>> pp;
  lowest_common_ancestor(vector<int> &p, vector<int> &d): d(d){
    int N = p.size();
    pp = vector<vector<int>>(LOG, vector<int>(N, -1));
    pp[0] = p;
    for (int i = 0; i < LOG - 1; i++){
      for (int j = 0; j < N; j++){
        if (pp[i][j] != -1){
          pp[i + 1][j] = pp[i][pp[i][j]];
        }
      }
    }
  }
  int la(int v, int x){
    for (int i = 0; i < LOG; i++){
      if ((x >> i & 1) == 1){
        v = pp[i][v];
      }
    }
    return v;
  }
  int lca(int v, int w){
    if (d[v] > d[w]){
      swap(v, w);
    }
    w = la(w, d[w] - d[v]);
    if (v == w){
      return v;
    } else {
      for (int i = LOG - 1; i >= 0; i--){
        if (pp[i][v] != pp[i][w]){
          v = pp[i][v];
          w = pp[i][w];
        }
      }
      return pp[0][v];
    }
  }
  int dist(int v, int w){
    return d[v] + d[w] - 2 * d[lca(v, w)];
  }
};
int main(){
  int N;
  cin >> N;
  vector<vector<int>> E(N);
  for (int i = 0; i < N - 1; i++){
    int a, b;
    cin >> a >> b;
    a--;
    b--;
    E[a].push_back(b);
    E[b].push_back(a);
  }
  vector<int> p(N, -1);
  vector<vector<int>> c(N);
  vector<int> d(N, 0);
  queue<int> q;
  q.push(0);
  vector<int> bfs;
  while (!q.empty()){
    int v = q.front();
    q.pop();
    bfs.push_back(v);
    for (int w : E[v]){
      if (w != p[v]){
        p[w] = v;
        c[v].push_back(w);
        d[w] = d[v] + 1;
        q.push(w);
      }
    }
  }
  vector<long long> POW(N);
  POW[0] = 1;
  for (int i = 0; i < N - 1; i++){
    POW[i + 1] = POW[i] * 2 % MOD;
  }
  reverse(bfs.begin(), bfs.end());
  vector<long long> dp1(N, 1);
  for (int v : bfs){
    for (int w : c[v]){
      dp1[v] += dp1[w] * 2;
      dp1[v] %= MOD;
    }
  }
  reverse(bfs.begin(), bfs.end());
  vector<long long> dp2(N, 1);
  for (int v : bfs){
    long long S = 0;
    for (int w : c[v]){
      S += dp1[w];
      S %= MOD;
    }
    for (int w : c[v]){
      dp2[w] += dp2[v] * 2;
      dp2[w] += (S - dp1[w] + MOD) * 4;
      dp2[w] %= MOD;
    }
  }
  lowest_common_ancestor T(p, d);
  int Q;
  cin >> Q;
  for (int i = 0; i < Q; i++){
    int x, y;
    cin >> x >> y;
    x--;
    y--;
    int z = T.lca(x, y);
    if (y == z){
      swap(x, y);
    }
    long long ans;
    if (x == z){
      int v = T.la(y, T.dist(x, y) - 1);
      long long S = (dp2[x] + dp1[x] - 1 - dp1[v] * 2 + MOD * 3) % MOD;
      ans = S * dp1[y] % MOD;
    } else {
      ans = dp1[x] * dp1[y] % MOD;
    }
    ans *= POW[T.dist(x, y)];
    ans %= MOD;
    cout << ans << endl;
  }
}
accuracy: 1
time_ms: 110
memory_kb: 24948
final_rank: 4

code_snippet: #include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <chrono>
#include <cmath>
#include <complex>
#include <deque>
#include <forward_list>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
using lint = long long;
using pint = pair<int, int>;
using plint = pair<lint, lint>;
struct fast_ios { fast_ios(){ cin.tie(nullptr), ios::sync_with_stdio(false), cout << fixed << setprecision(20); }; } fast_ios_;
#define ALL(x) (x).begin(), (x).end()
#define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i<i##_end_;i++)
#define IFOR(i, begin, end) for(int i=(end)-1,i##_begin_=(begin);i>=i##_begin_;i--)
#define REP(i, n) FOR(i,0,n)
#define IREP(i, n) IFOR(i,0,n)
template <typename T, typename V>
void ndarray(vector<T>& vec, const V& val, int len) { vec.assign(len, val); }
template <typename T, typename V, typename... Args> void ndarray(vector<T>& vec, const V& val, int len, Args... args) { vec.resize(len), for_each(begin(vec), end(vec), [&](T& v) { ndarray(v, val, args...); }); }
template <typename T> bool chmax(T &m, const T q) { return m < q ? (m = q, true) : false; }
template <typename T> bool chmin(T &m, const T q) { return m > q ? (m = q, true) : false; }
int floor_lg(long long x) { return x <= 0 ? -1 : 63 - __builtin_clzll(x); }
template <typename T1, typename T2> pair<T1, T2> operator+(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first + r.first, l.second + r.second); }
template <typename T1, typename T2> pair<T1, T2> operator-(const pair<T1, T2> &l, const pair<T1, T2> &r) { return make_pair(l.first - r.first, l.second - r.second); }
template <typename T> vector<T> sort_unique(vector<T> vec) { sort(vec.begin(), vec.end()), vec.erase(unique(vec.begin(), vec.end()), vec.end()); return vec; }
template <typename T> int arglb(const std::vector<T> &v, const T &x) { return std::distance(v.begin(), std::lower_bound(v.begin(), v.end(), x)); }
template <typename T> int argub(const std::vector<T> &v, const T &x) { return std::distance(v.begin(), std::upper_bound(v.begin(), v.end(), x)); }
template <typename T> istream &operator>>(istream &is, vector<T> &vec) { for (auto &v : vec) is >> v; return is; }
template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec) { os << '['; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <typename T, size_t sz> ostream &operator<<(ostream &os, const array<T, sz> &arr) { os << '['; for (auto v : arr) os << v << ','; os << ']'; return os; }
#if __cplusplus >= 201703L
template <typename... T> istream &operator>>(istream &is, tuple<T...> &tpl) { std::apply([&is](auto &&... args) { ((is >> args), ...);}, tpl); return is; }
template <typename... T> ostream &operator<<(ostream &os, const tuple<T...> &tpl) { os << '('; std::apply([&os](auto &&... args) { ((os << args << ','), ...);}, tpl); return os << ')'; }
#endif
template <typename T> ostream &operator<<(ostream &os, const deque<T> &vec) { os << "deq["; for (auto v : vec) os << v << ','; os << ']'; return os; }
template <typename T> ostream &operator<<(ostream &os, const set<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T, typename TH> ostream &operator<<(ostream &os, const unordered_set<T, TH> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T> ostream &operator<<(ostream &os, const multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T> ostream &operator<<(ostream &os, const unordered_multiset<T> &vec) { os << '{'; for (auto v : vec) os << v << ','; os << '}'; return os; }
template <typename T1, typename T2> ostream &operator<<(ostream &os, const pair<T1, T2> &pa) { os << '(' << pa.first << ',' << pa.second << ')'; return os; }
template <typename TK, typename TV> ostream &operator<<(ostream &os, const map<TK, TV> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
template <typename TK, typename TV, typename TH> ostream &operator<<(ostream &os, const unordered_map<TK, TV, TH> &mp) { os << '{'; for (auto v : mp) os << v.first << "=>" << v.second << ','; os << '}'; return os; }
#ifdef HITONANODE_LOCAL
const string COLOR_RESET = "\033[0m", BRIGHT_GREEN = "\033[1;32m", BRIGHT_RED = "\033[1;31m", BRIGHT_CYAN = "\033[1;36m", NORMAL_CROSSED = "\033[0;9;37m", RED_BACKGROUND = "\033[1;41m", NORMAL_FAINT = "\033[0;2m";
#define dbg(x) cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << endl
#define dbgif(cond, x) ((cond) ? cerr << BRIGHT_CYAN << #x << COLOR_RESET << " = " << (x) << NORMAL_FAINT << " (L" << __LINE__ << ") " << __FILE__ << COLOR_RESET << endl : cerr)
#else
#define dbg(x) 0
#define dbgif(cond, x) 0
#endif

template <int md> struct ModInt {
#if __cplusplus >= 201402L
#define MDCONST constexpr
#else
#define MDCONST
#endif
    using lint = long long;
    MDCONST static int mod() { return md; }
    static int get_primitive_root() {
        static int primitive_root = 0;
        if (!primitive_root) {
            primitive_root = [&]() {
                std::set<int> fac;
                int v = md - 1;
                for (lint i = 2; i * i <= v; i++)
                    while (v % i == 0) fac.insert(i), v /= i;
                if (v > 1) fac.insert(v);
                for (int g = 1; g < md; g++) {
                    bool ok = true;
                    for (auto i : fac)
                        if (ModInt(g).pow((md - 1) / i) == 1) {
                            ok = false;
                            break;
                        }
                    if (ok) return g;
                }
                return -1;
            }();
        }
        return primitive_root;
    }
    int val;
    MDCONST ModInt() : val(0) {}
    MDCONST ModInt &_setval(lint v) { return val = (v >= md ? v - md : v), *this; }
    MDCONST ModInt(lint v) { _setval(v % md + md); }
    MDCONST explicit operator bool() const { return val != 0; }
    MDCONST ModInt operator+(const ModInt &x) const { return ModInt()._setval((lint)val + x.val); }
    MDCONST ModInt operator-(const ModInt &x) const {
        return ModInt()._setval((lint)val - x.val + md);
    }
    MDCONST ModInt operator*(const ModInt &x) const {
        return ModInt()._setval((lint)val * x.val % md);
    }
    MDCONST ModInt operator/(const ModInt &x) const {
        return ModInt()._setval((lint)val * x.inv() % md);
    }
    MDCONST ModInt operator-() const { return ModInt()._setval(md - val); }
    MDCONST ModInt &operator+=(const ModInt &x) { return *this = *this + x; }
    MDCONST ModInt &operator-=(const ModInt &x) { return *this = *this - x; }
    MDCONST ModInt &operator*=(const ModInt &x) { return *this = *this * x; }
    MDCONST ModInt &operator/=(const ModInt &x) { return *this = *this / x; }
    friend MDCONST ModInt operator+(lint a, const ModInt &x) {
        return ModInt()._setval(a % md + x.val);
    }
    friend MDCONST ModInt operator-(lint a, const ModInt &x) {
        return ModInt()._setval(a % md - x.val + md);
    }
    friend MDCONST ModInt operator*(lint a, const ModInt &x) {
        return ModInt()._setval(a % md * x.val % md);
    }
    friend MDCONST ModInt operator/(lint a, const ModInt &x) {
        return ModInt()._setval(a % md * x.inv() % md);
    }
    MDCONST bool operator==(const ModInt &x) const { return val == x.val; }
    MDCONST bool operator!=(const ModInt &x) const { return val != x.val; }
    MDCONST bool operator<(const ModInt &x) const {
        return val < x.val;
    } // To use std::map<ModInt, T>
    friend std::istream &operator>>(std::istream &is, ModInt &x) {
        lint t;
        return is >> t, x = ModInt(t), is;
    }
    MDCONST friend std::ostream &operator<<(std::ostream &os, const ModInt &x) {
        return os << x.val;
    }
    MDCONST ModInt pow(lint n) const {
        ModInt ans = 1, tmp = *this;
        while (n) {
            if (n & 1) ans *= tmp;
            tmp *= tmp, n >>= 1;
        }
        return ans;
    }

    static std::vector<ModInt> facs, facinvs, invs;
    MDCONST static void _precalculation(int N) {
        int l0 = facs.size();
        if (N > md) N = md;
        if (N <= l0) return;
        facs.resize(N), facinvs.resize(N), invs.resize(N);
        for (int i = l0; i < N; i++) facs[i] = facs[i - 1] * i;
        facinvs[N - 1] = facs.back().pow(md - 2);
        for (int i = N - 2; i >= l0; i--) facinvs[i] = facinvs[i + 1] * (i + 1);
        for (int i = N - 1; i >= l0; i--) invs[i] = facinvs[i] * facs[i - 1];
    }
    MDCONST lint inv() const {
        if (this->val < std::min(md >> 1, 1 << 21)) {
            while (this->val >= int(facs.size())) _precalculation(facs.size() * 2);
            return invs[this->val].val;
        } else {
            return this->pow(md - 2).val;
        }
    }
    MDCONST ModInt fac() const {
        while (this->val >= int(facs.size())) _precalculation(facs.size() * 2);
        return facs[this->val];
    }
    MDCONST ModInt facinv() const {
        while (this->val >= int(facs.size())) _precalculation(facs.size() * 2);
        return facinvs[this->val];
    }
    MDCONST ModInt doublefac() const {
        lint k = (this->val + 1) / 2;
        return (this->val & 1) ? ModInt(k * 2).fac() / (ModInt(2).pow(k) * ModInt(k).fac())
                               : ModInt(k).fac() * ModInt(2).pow(k);
    }
    MDCONST ModInt nCr(const ModInt &r) const {
        return (this->val < r.val) ? 0 : this->fac() * (*this - r).facinv() * r.facinv();
    }
    MDCONST ModInt nPr(const ModInt &r) const {
        return (this->val < r.val) ? 0 : this->fac() * (*this - r).facinv();
    }

    ModInt sqrt() const {
        if (val == 0) return 0;
        if (md == 2) return val;
        if (pow((md - 1) / 2) != 1) return 0;
        ModInt b = 1;
        while (b.pow((md - 1) / 2) == 1) b += 1;
        int e = 0, m = md - 1;
        while (m % 2 == 0) m >>= 1, e++;
        ModInt x = pow((m - 1) / 2), y = (*this) * x * x;
        x *= (*this);
        ModInt z = b.pow(m);
        while (y != 1) {
            int j = 0;
            ModInt t = y;
            while (t != 1) j++, t *= t;
            z = z.pow(1LL << (e - j - 1));
            x *= z, z *= z, y *= z;
            e = j;
        }
        return ModInt(std::min(x.val, md - x.val));
    }
};
template <int md> std::vector<ModInt<md>> ModInt<md>::facs = {1};
template <int md> std::vector<ModInt<md>> ModInt<md>::facinvs = {1};
template <int md> std::vector<ModInt<md>> ModInt<md>::invs = {0};
using mint = ModInt<998244353>;


// Rerooting
// Reference:
// - https://atcoder.jp/contests/abc222/editorial/2749
// - https://null-mn.hatenablog.com/entry/2020/04/14/124151
template <class Edge, class St, class Ch, Ch (*merge)(Ch, Ch), Ch (*f)(St, int, Edge),
          St (*g)(Ch, int), Ch (*e)()>
struct rerooting {
    int n_;
    std::vector<int> par, visited;
    std::vector<std::vector<std::pair<int, Edge>>> to;
    std::vector<St> dp_subtree;
    std::vector<St> dp_par;
    std::vector<St> dpall;
    rerooting(const std::vector<std::vector<std::pair<int, Edge>>> &to_)
        : n_(to_.size()), par(n_, -1), visited(n_, 0), to(to_) {
        for (int i = 0; i < n_; ++i) dp_subtree.push_back(g(e(), i));
        dp_par = dpall = dp_subtree;
    }

    void run_connected(int root) {
        if (visited[root]) return;
        visited[root] = 1;
        std::vector<int> visorder{root};

        for (int t = 0; t < int(visorder.size()); ++t) {
            int now = visorder[t];
            for (const auto &edge : to[now]) {
                int nxt = edge.first;
                if (visited[nxt]) continue;
                visorder.push_back(nxt);
                visited[nxt] = 1;
                par[nxt] = now;
            }
        }

        for (int t = int(visorder.size()) - 1; t >= 0; --t) {
            int now = visorder[t];
            Ch ch = e();
            for (const auto &edge : to[now]) {
                int nxt = edge.first;
                if (nxt == par[now]) continue;
                ch = merge(ch, f(dp_subtree[nxt], nxt, edge.second));
            }
            dp_subtree[now] = g(ch, now);
        }

        std::vector<Ch> left;
        for (int now : visorder) {
            int m = int(to[now].size());
            left.assign(m + 1, e());
            for (int j = 0; j < m; j++) {
                int nxt = to[now][j].first;
                const St &st = (nxt == par[now] ? dp_par[now] : dp_subtree[nxt]);
                left[j + 1] = merge(left[j], f(st, nxt, to[now][j].second));
            }
            dpall[now] = g(left.back(), now);

            Ch rprod = e();
            for (int j = m - 1; j >= 0; --j) {
                int nxt = to[now][j].first;
                if (nxt != par[now]) dp_par[nxt] = g(merge(left[j], rprod), now);

                const St &st = (nxt == par[now] ? dp_par[now] : dp_subtree[nxt]);
                rprod = merge(f(st, nxt, to[now][j].second), rprod);
            }
        }
    }

    void run() {
        for (int i = 0; i < n_; ++i) {
            if (!visited[i]) run_connected(i);
        }
    }
};
/* Template:
struct Subtree {};
struct Child {};
struct Edge {};
Child e() { return Child(); }
Child merge(Child x, Child y) { return Child(); }
Child f(Subtree x, int ch_id, Edge edge) { return Child(); }
Subtree g(Child x, int v_id) { return Subtree(); }

vector<vector<pair<int, Edge>>> to;
rerooting<Edge, Subtree, Child, merge, f, g, e> tree(to);
*/

using Edge = int;
using Subtree = mint;
using Child = mint;
Child e() { return 0; }
Child merge(Child x, Child y) { return x + y; }
Child f(Subtree x, int ch_id, Edge edge) { return x * mint(2).pow(edge); }
Subtree g(Child x, int v_id) { return 1 + Subtree(x); }

// lowest common ancestor (LCA) for undirected weighted tree
template <typename T> struct UndirectedWeightedTree {
    int INVALID = -1;
    int V, lgV;
    int E;
    int root;
    std::vector<std::vector<std::pair<int, int>>> adj; // (nxt_vertex, edge_id)
    // vector<pint> edge; // edges[edge_id] = (vertex_id, vertex_id)
    std::vector<T> weight;     // w[edge_id]
    std::vector<int> par;      // parent_vertex_id[vertex_id]
    std::vector<int> depth;    // depth_from_root[vertex_id]
    std::vector<T> acc_weight; // w_sum_from_root[vertex_id]

    void _fix_root_dfs(int now, int prv, int prv_edge_id) {
        par[now] = prv;
        if (prv_edge_id != INVALID) acc_weight[now] = acc_weight[prv] + weight[prv_edge_id];
        for (auto nxt : adj[now])
            if (nxt.first != prv) {
                depth[nxt.first] = depth[now] + 1;
                _fix_root_dfs(nxt.first, now, nxt.second);
            }
    }

    UndirectedWeightedTree() = default;
    UndirectedWeightedTree(int N) : V(N), E(0), adj(N) {
        lgV = 1;
        while (1 << lgV < V) lgV++;
    }

    void add_edge(int u, int v, T w) {
        adj[u].emplace_back(v, E);
        adj[v].emplace_back(u, E);
        // edge.emplace_back(u, v);
        weight.emplace_back(w);
        E++;
    }

    std::vector<std::vector<int>> doubling;
    void _doubling_precalc() {
        doubling.assign(lgV, std::vector<int>(V));
        doubling[0] = par;
        for (int d = 0; d < lgV - 1; d++)
            for (int i = 0; i < V; i++) {
                if (doubling[d][i] == INVALID)
                    doubling[d + 1][i] = INVALID;
                else
                    doubling[d + 1][i] = doubling[d][doubling[d][i]];
            }
    }

    void fix_root(int r) {
        root = r;
        par.resize(V);
        depth.resize(V);
        depth[r] = 0;
        acc_weight.resize(V);
        acc_weight[r] = 0;
        _fix_root_dfs(root, INVALID, INVALID);
        _doubling_precalc();
    }

    int kth_parent(int x, int k) {
        if (depth[x] < k) return INVALID;
        for (int d = 0; d < lgV; d++) {
            if (x == INVALID) return INVALID;
            if (k & (1 << d)) x = doubling[d][x];
        }
        return x;
    }

    int lowest_common_ancestor(int u, int v) {
        if (depth[u] > depth[v]) std::swap(u, v);

        v = kth_parent(v, depth[v] - depth[u]);
        if (u == v) return u;
        for (int d = lgV - 1; d >= 0; d--) {
            if (doubling[d][u] != doubling[d][v]) u = doubling[d][u], v = doubling[d][v];
        }
        return par[u];
    }

    T path_length(int u, int v) {
        // Not distance, but the sum of weights
        int r = lowest_common_ancestor(u, v);
        return (acc_weight[u] - acc_weight[r]) + (acc_weight[v] - acc_weight[r]);
    }
};


int main() {
    int N;
    cin >> N;
    vector<vector<pair<int, int>>> to(N);
    UndirectedWeightedTree<int> graph(N);
    REP(e, N - 1) {
        int a, b;
        cin >> a >> b;
        --a, --b;
        graph.add_edge(a, b, 1);
        to[a].emplace_back(b, 1), to[b].emplace_back(a, 1);
    }
    graph.fix_root(0);
    rerooting<Edge, Subtree, Child, merge, f, g, e> tree(to);
    tree.run_connected(0);
    dbg(tree.dp_subtree);
    dbg(tree.dp_par);
    dbg(tree.dpall);

    int Q;
    cin >> Q;
    while (Q--) {
        int x, y;
        cin >> x >> y;
        --x, --y;
        int l = graph.lowest_common_ancestor(x, y);
        if (l == y) swap(x, y);
        mint ret = mint(2).pow(graph.path_length(x, y));

        dbg(make_tuple(x, y, l));

        if (l == x) {
            int d = graph.path_length(x, y);
            int c = graph.kth_parent(y, d - 1);
            dbg(c);
            ret *= tree.dp_subtree[y] * (tree.dpall[x] - tree.dp_subtree[c] * 2);
        } else {
            ret *= tree.dp_subtree[x] * tree.dp_subtree[y];
        }
        cout << ret << '\n';
    }
}
accuracy: 1
time_ms: 70
memory_kb: 36852
final_rank: 7

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

using namespace std;

/**
 * @brief 何でもできるLCT
 */
template< typename LInfo, typename Lazy >
struct SplayTree {
  struct Node {
    Node *l, *r, *p;
    LInfo info;
    Lazy lazy, lbuf;

    explicit Node(const LInfo &info) : info(info), l(nullptr), r(nullptr),
                                       p(nullptr), lazy(Lazy()), lbuf(Lazy()) {}
  };

  const LInfo e;

  SplayTree() : e(LInfo()) {}

  using NP = Node *;

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  const LInfo &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r));
  }

  NP get_right(NP t) {
    while(t->r) t = t->r;
    return t;
  }

  NP alloc(const LInfo &v) {
    auto t = new Node(v);
    update(t);
    return t;
  }

  void propagate(NP t, const Lazy &lazy) {
    t->info.propagate(lazy);
    t->lbuf.propagate(lazy);
    t->lazy.propagate(lazy);
  }

  void push(NP t) {
    if(t->l) propagate(t->l, t->lazy);
    if(t->r) propagate(t->r, t->lazy);
    t->lazy = Lazy();
  }

  void splay(NP t) {
    push(t);
    while(t->p) {
      NP q = t->p;
      if(!q->p) {
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP insert(NP t, const LInfo &v) {
    if(not t) {
      t = alloc(v);
      return t;
    } else {
      NP cur = get_right(t), z = alloc(v);
      splay(cur);
      z->p = cur;
      cur->r = z;
      update(cur);
      splay(z);
      return z;
    }
  }

  NP erase(NP t) {
    splay(t);
    NP x = t->l, y = t->r;
    delete t;
    if(not x) {
      t = y;
      if(t) t->p = nullptr;
    } else if(not y) {
      t = x;
      t->p = nullptr;
    } else {
      x->p = nullptr;
      t = get_right(x);
      splay(t);
      t->r = y;
      y->p = t;
      update(t);
    }
    return t;
  }
};

template< template< typename, typename > typename _Info,
    template< typename > typename _LInfo, typename Lazy >
struct SuperLinkCutTree {
  using LInfo = _LInfo< Lazy >;
  using Info = _Info< LInfo, Lazy >;

private:
  struct Node {
    Node *l, *r, *p;
    Info info;
    typename SplayTree< LInfo, Lazy >::Node *light, *belong;
    bool rev;
    Lazy hlazy, llazy;

    bool is_root() const {
      return not p or (p->l != this and p->r != this);
    }

    explicit Node(const Info &info)
        : info(info), l(nullptr), r(nullptr), p(nullptr), rev(false),
          light(nullptr), belong(nullptr), hlazy(Lazy()), llazy(Lazy()) {}
  };

public:
  using NP = Node *;
  SplayTree< LInfo, Lazy > splay_tree;

private:
  const Info e;

private:
  void toggle(NP t) {
    swap(t->l, t->r);
    t->info.toggle();
    t->rev ^= true;
  }

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void propagate_heavy(NP t, const Lazy &hlazy) {
    t->hlazy.propagate(hlazy);
    t->info.propagate(hlazy);
  }

  void propagate_light(NP t, const Lazy &llazy) {
    t->llazy.propagate(llazy);
    t->info.propagate_light(llazy);
  }

  void propagate_all(NP t, const Lazy &lazy) {
    propagate_heavy(t, lazy);
    propagate_light(t, lazy);
  }

public:
  SuperLinkCutTree() : e{Info()}, splay_tree{} {}

  void push(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
    {
      if(t->l) {
        propagate_heavy(t->l, t->hlazy);
        propagate_light(t->l, t->llazy);
      }
      if(t->r) {
        propagate_heavy(t->r, t->hlazy);
        propagate_light(t->r, t->llazy);
      }
      if(t->light) {
        splay_tree.propagate(t->light, t->llazy);
      }
      t->hlazy = Lazy();
      t->llazy = Lazy();
    }
  }

  void push_rev(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
  }

  const Info &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r), splay_tree.get_info(t->light));
  }

  void splay(NP t) {
    push(t);
    {
      NP rot = t;
      while(not rot->is_root()) rot = rot->p;
      t->belong = rot->belong;
      if(t != rot) rot->belong = nullptr;
    }
    while(not t->is_root()) {
      NP q = t->p;
      if(q->is_root()) {
        push_rev(q), push_rev(t);
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        push_rev(r), push_rev(q), push_rev(t);
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP expose(NP t) {
    NP rp = nullptr;
    for(NP cur = t; cur; cur = cur->p) {
      splay(cur);
      if(cur->r) {
        cur->light = splay_tree.insert(cur->light, cur->r->info.link());
        cur->r->belong = cur->light;
      }
      cur->r = rp;
      if(cur->r) {
        splay_tree.splay(cur->r->belong);
        propagate_all(cur->r, cur->r->belong->lbuf);
        push(cur->r);
        cur->light = splay_tree.erase(cur->r->belong);
      }
      update(cur);
      rp = cur;
    }
    splay(t);
    return rp;
  }

  void link(NP child, NP parent) {
    expose(parent);
    expose(child);
    child->p = parent;
    parent->r = child;
    update(parent);
  }

  void cut(NP child) {
    expose(child);
    NP parent = child->l;
    child->l = nullptr;
    parent->p = nullptr;
    update(child);
  }

  void evert(NP t) {
    expose(t);
    toggle(t);
    push(t);
  }

  NP alloc(const Info &info) {
    NP t = new Node(info);
    update(t);
    return t;
  }

  bool is_connected(NP u, NP v) {
    expose(u), expose(v);
    return u == v or u->p;
  }

  vector< NP > build(vector< Info > &vs) {
    vector< NP > nodes(vs.size());
    for(int i = 0; i < (int) vs.size(); i++) {
      nodes[i] = alloc(vs[i]);
    }
    return nodes;
  }

  NP lca(NP u, NP v) {
    if(not is_connected(u, v)) return nullptr;
    expose(u);
    return expose(v);
  }

  void set_key(NP t, const Info &v) {
    expose(t);
    t->info = move(v);
    update(t);
  }

  void set_propagate_path(NP t, const Lazy &lazy) {
    expose(t);
    propagate_heavy(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_path(NP u, NP v, const Lazy &lazy) {
    evert(u);
    set_propagate_path(v, lazy);
  }

  void set_propagate_all(NP t, const Lazy &lazy) {
    expose(t);
    propagate_all(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_subtree(NP t, const Lazy &lazy) {
    expose(t);
    NP l = t->l;
    t->l = nullptr;
    propagate_all(t, lazy);
    push(t);
    t->l = l;
    update(t);
  }

  const Info &query(NP u) {
    expose(u);
    return get_info(u);
  }

  const Info &query_path(NP u, NP v) {
    evert(u);
    expose(v);
    return get_info(v);
  }

  Info query_subtree(NP u) {
    expose(u);
    NP l = u->l;
    u->l = nullptr;
    update(u);
    auto ret = u->info;
    u->l = l;
    update(u);
    return ret;
  }
};


/**
 * @brief Montgomery ModInt
 */
template< uint32_t mod, bool fast = false >
struct MontgomeryModInt {
  using mint = MontgomeryModInt;
  using i32 = int32_t;
  using i64 = int64_t;
  using u32 = uint32_t;
  using u64 = uint64_t;

  static constexpr u32 get_r() {
    u32 ret = mod;
    for(i32 i = 0; i < 4; i++) ret *= 2 - mod * ret;
    return ret;
  }

  static constexpr u32 r = get_r();
  static constexpr u32 n2 = -u64(mod) % mod;

  static_assert(r * mod == 1, "invalid, r * mod != 1");
  static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30");
  static_assert((mod & 1) == 1, "invalid, mod % 2 == 0");

  u32 x;

  MontgomeryModInt() : x{} {}

  MontgomeryModInt(const i64 &a)
      : x(reduce(u64(fast ? a : (a % mod + mod)) * n2)) {}

  static constexpr u32 reduce(const u64 &b) {
    return u32(b >> 32) + mod - u32((u64(u32(b) * r) * mod) >> 32);
  }

  mint &operator+=(const mint &p) {
    if(i32(x += p.x - 2 * mod) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator-=(const mint &p) {
    if(i32(x -= p.x) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator*=(const mint &p) {
    x = reduce(u64(x) * p.x);
    return *this;
  }

  mint &operator/=(const mint &p) {
    *this *= p.inverse();
    return *this;
  }

  mint operator-() const { return mint() - *this; }

  mint operator+(const mint &p) const { return mint(*this) += p; }

  mint operator-(const mint &p) const { return mint(*this) -= p; }

  mint operator*(const mint &p) const { return mint(*this) *= p; }

  mint operator/(const mint &p) const { return mint(*this) /= p; }

  bool operator==(const mint &p) const { return (x >= mod ? x - mod : x) == (p.x >= mod ? p.x - mod : p.x); }

  bool operator!=(const mint &p) const { return (x >= mod ? x - mod : x) != (p.x >= mod ? p.x - mod : p.x); }

  u32 get() const {
    u32 ret = reduce(x);
    return ret >= mod ? ret - mod : ret;
  }

  mint pow(u64 n) const {
    mint ret(1), mul(*this);
    while(n > 0) {
      if(n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }

  mint inverse() const {
    return pow(mod - 2);
  }

  friend ostream &operator<<(ostream &os, const mint &p) {
    return os << p.get();
  }

  friend istream &operator>>(istream &is, mint &a) {
    i64 t;
    is >> t;
    a = mint(t);
    return is;
  }

  static u32 get_mod() { return mod; }
};

constexpr int mod = 998244353;
using modint = MontgomeryModInt< mod >;

using T = modint;

// 遅延伝搬をするための作用素
struct Lazy {

  // 単位元
  Lazy() {}

  // 初期化
  Lazy(T v) {}

  // 遅延伝搬
  void propagate(const Lazy &p) {}
};

// Light-edge の情報
template< typename Lazy >
struct LInfo {
  T v, sum;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  LInfo() : sum(0) {}

  // 初期化
  LInfo(T v) : v(v) {}

  // l, r は Splay-tree の子 (原理上、各ノード区別はない)
  void update(const LInfo &l, const LInfo &r) {
    sum = l.sum + r.sum + v;
  }

  // 部分木への遅延伝搬
  void propagate(const Lazy &p) {}
};

// Heavy-edge の情報
template< typename LInfo, typename Lazy >
struct Info {
  T ans, pall, call;
  int len;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  Info() : ans(0), pall(0), call(0), len(0) {}

  // 初期化
  Info(T v) {}

  // 反転
  void toggle() { swap(pall, call); }

  // pが親, cがheavy-edgeで結ばれた子, lがそれ以外の子
  void update(const Info &p, const Info &c, const LInfo &l) {
    modint xs = modint(2).pow(p.len);
    modint ys = modint(2).pow(c.len);
    ans = l.sum * 2 + 1;
    pall = p.pall + (c.pall + 1 + l.sum) * xs;
    call = c.call + (p.call + 1 + l.sum) * ys;
    len = p.len + c.len + 1;
  }

  // 親と light-edge で繋げる
  LInfo link() const { return LInfo(call); }

  // 遅延伝搬
  void propagate(const Lazy &p) {}

  // light-edgeに対する遅延伝搬
  // pathとsubtreeの遅延伝搬が両方ある場合に実装する
  void propagate_light(const Lazy &p) {}
};

using LCT = SuperLinkCutTree< Info, LInfo, Lazy >;


int main() {
  int N, Q;
  cin >> N;
  LCT lct;
  vector< LCT::NP > vs(N);
  for(int i = 0; i < N; i++) {
    vs[i] = lct.alloc(Info< LInfo< Lazy >, Lazy >(0));
  }
  for(int i = 1; i < N; i++) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    lct.evert(vs[a]);
    lct.link(vs[a], vs[b]);
  }
  cin >> Q;
  while(Q--) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    auto x = lct.query_path(vs[a], vs[b]).ans;
    auto y = lct.query_path(vs[b], vs[a]).ans;
    cout << modint(2).pow(lct.get_info(vs[a]).len - 1) * x * y << "\n";
  }
}
accuracy: 0.09090909090909091
time_ms: 440
memory_kb: 14636
final_rank: 20

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

using namespace std;

/**
 * @brief 何でもできるLCT
 */
template< typename LInfo, typename Lazy >
struct SplayTree {
  struct Node {
    Node *l, *r, *p;
    LInfo info;
    Lazy lazy, lbuf;

    explicit Node(const LInfo &info) : info(info), l(nullptr), r(nullptr),
                                       p(nullptr), lazy(Lazy()), lbuf(Lazy()) {}
  };

  const LInfo e;

  SplayTree() : e(LInfo()) {}

  using NP = Node *;

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  const LInfo &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r));
  }

  NP get_right(NP t) {
    while(t->r) t = t->r;
    return t;
  }

  NP alloc(const LInfo &v) {
    auto t = new Node(v);
    update(t);
    return t;
  }

  void propagate(NP t, const Lazy &lazy) {
    t->info.propagate(lazy);
    t->lbuf.propagate(lazy);
    t->lazy.propagate(lazy);
  }

  void push(NP t) {
    if(t->l) propagate(t->l, t->lazy);
    if(t->r) propagate(t->r, t->lazy);
    t->lazy = Lazy();
  }

  void splay(NP t) {
    push(t);
    while(t->p) {
      NP q = t->p;
      if(!q->p) {
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP insert(NP t, const LInfo &v) {
    if(not t) {
      t = alloc(v);
      return t;
    } else {
      NP cur = get_right(t), z = alloc(v);
      splay(cur);
      z->p = cur;
      cur->r = z;
      update(cur);
      splay(z);
      return z;
    }
  }

  NP erase(NP t) {
    splay(t);
    NP x = t->l, y = t->r;
    delete t;
    if(not x) {
      t = y;
      if(t) t->p = nullptr;
    } else if(not y) {
      t = x;
      t->p = nullptr;
    } else {
      x->p = nullptr;
      t = get_right(x);
      splay(t);
      t->r = y;
      y->p = t;
      update(t);
    }
    return t;
  }
};

template< template< typename, typename > typename _Info,
    template< typename > typename _LInfo, typename Lazy >
struct SuperLinkCutTree {
  using LInfo = _LInfo< Lazy >;
  using Info = _Info< LInfo, Lazy >;

private:
  struct Node {
    Node *l, *r, *p;
    Info info;
    typename SplayTree< LInfo, Lazy >::Node *light, *belong;
    bool rev;
    Lazy hlazy, llazy;

    bool is_root() const {
      return not p or (p->l != this and p->r != this);
    }

    explicit Node(const Info &info)
        : info(info), l(nullptr), r(nullptr), p(nullptr), rev(false),
          light(nullptr), belong(nullptr), hlazy(Lazy()), llazy(Lazy()) {}
  };

public:
  using NP = Node *;
  SplayTree< LInfo, Lazy > splay_tree;

private:
  const Info e;

private:
  void toggle(NP t) {
    swap(t->l, t->r);
    t->info.toggle();
    t->rev ^= true;
  }

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void propagate_heavy(NP t, const Lazy &hlazy) {
    t->hlazy.propagate(hlazy);
    t->info.propagate(hlazy);
  }

  void propagate_light(NP t, const Lazy &llazy) {
    t->llazy.propagate(llazy);
    t->info.propagate_light(llazy);
  }

  void propagate_all(NP t, const Lazy &lazy) {
    propagate_heavy(t, lazy);
    propagate_light(t, lazy);
  }

public:
  SuperLinkCutTree() : e{Info()}, splay_tree{} {}

  void push(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
    {
      if(t->l) {
        propagate_heavy(t->l, t->hlazy);
        propagate_light(t->l, t->llazy);
      }
      if(t->r) {
        propagate_heavy(t->r, t->hlazy);
        propagate_light(t->r, t->llazy);
      }
      if(t->light) {
        splay_tree.propagate(t->light, t->llazy);
      }
      t->hlazy = Lazy();
      t->llazy = Lazy();
    }
  }

  void push_rev(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
  }

  const Info &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r), splay_tree.get_info(t->light));
  }

  void splay(NP t) {
    push(t);
    {
      NP rot = t;
      while(not rot->is_root()) rot = rot->p;
      t->belong = rot->belong;
      if(t != rot) rot->belong = nullptr;
    }
    while(not t->is_root()) {
      NP q = t->p;
      if(q->is_root()) {
        push_rev(q), push_rev(t);
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        push_rev(r), push_rev(q), push_rev(t);
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP expose(NP t) {
    NP rp = nullptr;
    for(NP cur = t; cur; cur = cur->p) {
      splay(cur);
      if(cur->r) {
        cur->light = splay_tree.insert(cur->light, cur->r->info.link());
        cur->r->belong = cur->light;
      }
      cur->r = rp;
      if(cur->r) {
        splay_tree.splay(cur->r->belong);
        propagate_all(cur->r, cur->r->belong->lbuf);
        push(cur->r);
        cur->light = splay_tree.erase(cur->r->belong);
      }
      update(cur);
      rp = cur;
    }
    splay(t);
    return rp;
  }

  void link(NP child, NP parent) {
    expose(parent);
    expose(child);
    child->p = parent;
    parent->r = child;
    update(parent);
  }

  void cut(NP child) {
    expose(child);
    NP parent = child->l;
    child->l = nullptr;
    parent->p = nullptr;
    update(child);
  }

  void evert(NP t) {
    expose(t);
    toggle(t);
    push(t);
  }

  NP alloc(const Info &info) {
    NP t = new Node(info);
    update(t);
    return t;
  }

  bool is_connected(NP u, NP v) {
    expose(u), expose(v);
    return u == v or u->p;
  }

  vector< NP > build(vector< Info > &vs) {
    vector< NP > nodes(vs.size());
    for(int i = 0; i < (int) vs.size(); i++) {
      nodes[i] = alloc(vs[i]);
    }
    return nodes;
  }

  NP lca(NP u, NP v) {
    if(not is_connected(u, v)) return nullptr;
    expose(u);
    return expose(v);
  }

  void set_key(NP t, const Info &v) {
    expose(t);
    t->info = move(v);
    update(t);
  }

  void set_propagate_path(NP t, const Lazy &lazy) {
    expose(t);
    propagate_heavy(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_path(NP u, NP v, const Lazy &lazy) {
    evert(u);
    set_propagate_path(v, lazy);
  }

  void set_propagate_all(NP t, const Lazy &lazy) {
    expose(t);
    propagate_all(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_subtree(NP t, const Lazy &lazy) {
    expose(t);
    NP l = t->l;
    t->l = nullptr;
    propagate_all(t, lazy);
    push(t);
    t->l = l;
    update(t);
  }

  const Info &query(NP u) {
    expose(u);
    return get_info(u);
  }

  const Info &query_path(NP u, NP v) {
    evert(u);
    expose(v);
    return get_info(v);
  }

  Info query_subtree(NP u) {
    expose(u);
    NP l = u->l;
    u->l = nullptr;
    update(u);
    auto ret = u->info;
    u->l = l;
    update(u);
    return ret;
  }
};


/**
 * @brief Montgomery ModInt
 */
template< uint32_t mod, bool fast = false >
struct MontgomeryModInt {
  using mint = MontgomeryModInt;
  using i32 = int32_t;
  using i64 = int64_t;
  using u32 = uint32_t;
  using u64 = uint64_t;

  static constexpr u32 get_r() {
    u32 ret = mod;
    for(i32 i = 0; i < 4; i++) ret *= 2 - mod * ret;
    return ret;
  }

  static constexpr u32 r = get_r();
  static constexpr u32 n2 = -u64(mod) % mod;

  static_assert(r * mod == 1, "invalid, r * mod != 1");
  static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30");
  static_assert((mod & 1) == 1, "invalid, mod % 2 == 0");

  u32 x;

  MontgomeryModInt() : x{} {}

  MontgomeryModInt(const i64 &a)
      : x(reduce(u64(fast ? a : (a % mod + mod)) * n2)) {}

  static constexpr u32 reduce(const u64 &b) {
    return u32(b >> 32) + mod - u32((u64(u32(b) * r) * mod) >> 32);
  }

  mint &operator+=(const mint &p) {
    if(i32(x += p.x - 2 * mod) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator-=(const mint &p) {
    if(i32(x -= p.x) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator*=(const mint &p) {
    x = reduce(u64(x) * p.x);
    return *this;
  }

  mint &operator/=(const mint &p) {
    *this *= p.inverse();
    return *this;
  }

  mint operator-() const { return mint() - *this; }

  mint operator+(const mint &p) const { return mint(*this) += p; }

  mint operator-(const mint &p) const { return mint(*this) -= p; }

  mint operator*(const mint &p) const { return mint(*this) *= p; }

  mint operator/(const mint &p) const { return mint(*this) /= p; }

  bool operator==(const mint &p) const { return (x >= mod ? x - mod : x) == (p.x >= mod ? p.x - mod : p.x); }

  bool operator!=(const mint &p) const { return (x >= mod ? x - mod : x) != (p.x >= mod ? p.x - mod : p.x); }

  u32 get() const {
    u32 ret = reduce(x);
    return ret >= mod ? ret - mod : ret;
  }

  mint pow(u64 n) const {
    mint ret(1), mul(*this);
    while(n > 0) {
      if(n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }

  mint inverse() const {
    return pow(mod - 2);
  }

  friend ostream &operator<<(ostream &os, const mint &p) {
    return os << p.get();
  }

  friend istream &operator>>(istream &is, mint &a) {
    i64 t;
    is >> t;
    a = mint(t);
    return is;
  }

  static u32 get_mod() { return mod; }
};

constexpr int mod = 998244353;
using modint = MontgomeryModInt< mod >;

using T = modint;

// 遅延伝搬をするための作用素
struct Lazy {

  // 単位元
  Lazy() {}

  // 初期化
  Lazy(T v) {}

  // 遅延伝搬
  void propagate(const Lazy &p) {}
};

// Light-edge の情報
template< typename Lazy >
struct LInfo {
  T v, sum;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  LInfo() : sum(0) {}

  // 初期化
  LInfo(T v) : v(v) {}

  // l, r は Splay-tree の子 (原理上、各ノード区別はない)
  void update(const LInfo &l, const LInfo &r) {
    sum = l.sum + r.sum + v;
  }

  // 部分木への遅延伝搬
  void propagate(const Lazy &p) {}
};

// Heavy-edge の情報
template< typename LInfo, typename Lazy >
struct Info {
  T ans, pall, call;
  int len;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  Info() : ans(0), pall(0), call(0), len(0) {}

  // 初期化
  Info(T v) {}

  // 反転
  void toggle() { swap(pall, call); }

  // pが親, cがheavy-edgeで結ばれた子, lがそれ以外の子
  void update(const Info &p, const Info &c, const LInfo &l) {
    ans = l.sum * 2 + 1;
    pall = p.pall + c.pall * 2 + ans;
    call = c.call + p.call * 2 + ans;
    len = p.len + c.len + 1;
  }

  // 親と light-edge で繋げる
  LInfo link() const { return LInfo(pall); }

  // 遅延伝搬
  void propagate(const Lazy &p) {}

  // light-edgeに対する遅延伝搬
  // pathとsubtreeの遅延伝搬が両方ある場合に実装する
  void propagate_light(const Lazy &p) {}
};

using LCT = SuperLinkCutTree< Info, LInfo, Lazy >;


int main() {
  int N, Q;
  cin >> N;
  LCT lct;
  vector< LCT::NP > vs(N);
  for(int i = 0; i < N; i++) {
    vs[i] = lct.alloc(Info< LInfo< Lazy >, Lazy >(0));
  }
  for(int i = 1; i < N; i++) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    lct.evert(vs[a]);
    lct.link(vs[a], vs[b]);
  }
  cin >> Q;
  while(Q--) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    auto x = lct.query_path(vs[a], vs[b]).ans;
    auto y = lct.query_path(vs[b], vs[a]).ans;
    cout << modint(2).pow(lct.get_info(vs[a]).len - 1) * x * y << "\n";
  }
}
accuracy: 0.09090909090909091
time_ms: 250
memory_kb: 14672
final_rank: 17

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

using namespace std;

/**
 * @brief 何でもできるLCT
 */
template< typename LInfo, typename Lazy >
struct SplayTree {
  struct Node {
    Node *l, *r, *p;
    LInfo info;
    Lazy lazy, lbuf;

    explicit Node(const LInfo &info) : info(info), l(nullptr), r(nullptr),
                                       p(nullptr), lazy(Lazy()), lbuf(Lazy()) {}
  };

  const LInfo e;

  SplayTree() : e(LInfo()) {}

  using NP = Node *;

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  const LInfo &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r));
  }

  NP get_right(NP t) {
    while(t->r) t = t->r;
    return t;
  }

  NP alloc(const LInfo &v) {
    auto t = new Node(v);
    update(t);
    return t;
  }

  void propagate(NP t, const Lazy &lazy) {
    t->info.propagate(lazy);
    t->lbuf.propagate(lazy);
    t->lazy.propagate(lazy);
  }

  void push(NP t) {
    if(t->l) propagate(t->l, t->lazy);
    if(t->r) propagate(t->r, t->lazy);
    t->lazy = Lazy();
  }

  void splay(NP t) {
    push(t);
    while(t->p) {
      NP q = t->p;
      if(!q->p) {
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP insert(NP t, const LInfo &v) {
    if(not t) {
      t = alloc(v);
      return t;
    } else {
      NP cur = get_right(t), z = alloc(v);
      splay(cur);
      z->p = cur;
      cur->r = z;
      update(cur);
      splay(z);
      return z;
    }
  }

  NP erase(NP t) {
    splay(t);
    NP x = t->l, y = t->r;
    delete t;
    if(not x) {
      t = y;
      if(t) t->p = nullptr;
    } else if(not y) {
      t = x;
      t->p = nullptr;
    } else {
      x->p = nullptr;
      t = get_right(x);
      splay(t);
      t->r = y;
      y->p = t;
      update(t);
    }
    return t;
  }
};

template< template< typename, typename > typename _Info,
    template< typename > typename _LInfo, typename Lazy >
struct SuperLinkCutTree {
  using LInfo = _LInfo< Lazy >;
  using Info = _Info< LInfo, Lazy >;

private:
  struct Node {
    Node *l, *r, *p;
    Info info;
    typename SplayTree< LInfo, Lazy >::Node *light, *belong;
    bool rev;
    Lazy hlazy, llazy;

    bool is_root() const {
      return not p or (p->l != this and p->r != this);
    }

    explicit Node(const Info &info)
        : info(info), l(nullptr), r(nullptr), p(nullptr), rev(false),
          light(nullptr), belong(nullptr), hlazy(Lazy()), llazy(Lazy()) {}
  };

public:
  using NP = Node *;
  SplayTree< LInfo, Lazy > splay_tree;

private:
  const Info e;

private:
  void toggle(NP t) {
    swap(t->l, t->r);
    t->info.toggle();
    t->rev ^= true;
  }

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void propagate_heavy(NP t, const Lazy &hlazy) {
    t->hlazy.propagate(hlazy);
    t->info.propagate(hlazy);
  }

  void propagate_light(NP t, const Lazy &llazy) {
    t->llazy.propagate(llazy);
    t->info.propagate_light(llazy);
  }

  void propagate_all(NP t, const Lazy &lazy) {
    propagate_heavy(t, lazy);
    propagate_light(t, lazy);
  }

public:
  SuperLinkCutTree() : e{Info()}, splay_tree{} {}

  void push(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
    {
      if(t->l) {
        propagate_heavy(t->l, t->hlazy);
        propagate_light(t->l, t->llazy);
      }
      if(t->r) {
        propagate_heavy(t->r, t->hlazy);
        propagate_light(t->r, t->llazy);
      }
      if(t->light) {
        splay_tree.propagate(t->light, t->llazy);
      }
      t->hlazy = Lazy();
      t->llazy = Lazy();
    }
  }

  void push_rev(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
  }

  const Info &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r), splay_tree.get_info(t->light));
  }

  void splay(NP t) {
    push(t);
    {
      NP rot = t;
      while(not rot->is_root()) rot = rot->p;
      t->belong = rot->belong;
      if(t != rot) rot->belong = nullptr;
    }
    while(not t->is_root()) {
      NP q = t->p;
      if(q->is_root()) {
        push_rev(q), push_rev(t);
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        push_rev(r), push_rev(q), push_rev(t);
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP expose(NP t) {
    NP rp = nullptr;
    for(NP cur = t; cur; cur = cur->p) {
      splay(cur);
      if(cur->r) {
        cur->light = splay_tree.insert(cur->light, cur->r->info.link());
        cur->r->belong = cur->light;
      }
      cur->r = rp;
      if(cur->r) {
        splay_tree.splay(cur->r->belong);
        propagate_all(cur->r, cur->r->belong->lbuf);
        push(cur->r);
        cur->light = splay_tree.erase(cur->r->belong);
      }
      update(cur);
      rp = cur;
    }
    splay(t);
    return rp;
  }

  void link(NP child, NP parent) {
    expose(parent);
    expose(child);
    child->p = parent;
    parent->r = child;
    update(parent);
  }

  void cut(NP child) {
    expose(child);
    NP parent = child->l;
    child->l = nullptr;
    parent->p = nullptr;
    update(child);
  }

  void evert(NP t) {
    expose(t);
    toggle(t);
    push(t);
  }

  NP alloc(const Info &info) {
    NP t = new Node(info);
    update(t);
    return t;
  }

  bool is_connected(NP u, NP v) {
    expose(u), expose(v);
    return u == v or u->p;
  }

  vector< NP > build(vector< Info > &vs) {
    vector< NP > nodes(vs.size());
    for(int i = 0; i < (int) vs.size(); i++) {
      nodes[i] = alloc(vs[i]);
    }
    return nodes;
  }

  NP lca(NP u, NP v) {
    if(not is_connected(u, v)) return nullptr;
    expose(u);
    return expose(v);
  }

  void set_key(NP t, const Info &v) {
    expose(t);
    t->info = move(v);
    update(t);
  }

  void set_propagate_path(NP t, const Lazy &lazy) {
    expose(t);
    propagate_heavy(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_path(NP u, NP v, const Lazy &lazy) {
    evert(u);
    set_propagate_path(v, lazy);
  }

  void set_propagate_all(NP t, const Lazy &lazy) {
    expose(t);
    propagate_all(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_subtree(NP t, const Lazy &lazy) {
    expose(t);
    NP l = t->l;
    t->l = nullptr;
    propagate_all(t, lazy);
    push(t);
    t->l = l;
    update(t);
  }

  const Info &query(NP u) {
    expose(u);
    return get_info(u);
  }

  const Info &query_path(NP u, NP v) {
    evert(u);
    expose(v);
    return get_info(v);
  }

  Info query_subtree(NP u) {
    expose(u);
    NP l = u->l;
    u->l = nullptr;
    update(u);
    auto ret = u->info;
    u->l = l;
    update(u);
    return ret;
  }
};


/**
 * @brief Montgomery ModInt
 */
template< uint32_t mod, bool fast = false >
struct MontgomeryModInt {
  using mint = MontgomeryModInt;
  using i32 = int32_t;
  using i64 = int64_t;
  using u32 = uint32_t;
  using u64 = uint64_t;

  static constexpr u32 get_r() {
    u32 ret = mod;
    for(i32 i = 0; i < 4; i++) ret *= 2 - mod * ret;
    return ret;
  }

  static constexpr u32 r = get_r();
  static constexpr u32 n2 = -u64(mod) % mod;

  static_assert(r * mod == 1, "invalid, r * mod != 1");
  static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30");
  static_assert((mod & 1) == 1, "invalid, mod % 2 == 0");

  u32 x;

  MontgomeryModInt() : x{} {}

  MontgomeryModInt(const i64 &a)
      : x(reduce(u64(fast ? a : (a % mod + mod)) * n2)) {}

  static constexpr u32 reduce(const u64 &b) {
    return u32(b >> 32) + mod - u32((u64(u32(b) * r) * mod) >> 32);
  }

  mint &operator+=(const mint &p) {
    if(i32(x += p.x - 2 * mod) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator-=(const mint &p) {
    if(i32(x -= p.x) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator*=(const mint &p) {
    x = reduce(u64(x) * p.x);
    return *this;
  }

  mint &operator/=(const mint &p) {
    *this *= p.inverse();
    return *this;
  }

  mint operator-() const { return mint() - *this; }

  mint operator+(const mint &p) const { return mint(*this) += p; }

  mint operator-(const mint &p) const { return mint(*this) -= p; }

  mint operator*(const mint &p) const { return mint(*this) *= p; }

  mint operator/(const mint &p) const { return mint(*this) /= p; }

  bool operator==(const mint &p) const { return (x >= mod ? x - mod : x) == (p.x >= mod ? p.x - mod : p.x); }

  bool operator!=(const mint &p) const { return (x >= mod ? x - mod : x) != (p.x >= mod ? p.x - mod : p.x); }

  u32 get() const {
    u32 ret = reduce(x);
    return ret >= mod ? ret - mod : ret;
  }

  mint pow(u64 n) const {
    mint ret(1), mul(*this);
    while(n > 0) {
      if(n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }

  mint inverse() const {
    return pow(mod - 2);
  }

  friend ostream &operator<<(ostream &os, const mint &p) {
    return os << p.get();
  }

  friend istream &operator>>(istream &is, mint &a) {
    i64 t;
    is >> t;
    a = mint(t);
    return is;
  }

  static u32 get_mod() { return mod; }
};

constexpr int mod = 998244353;
using modint = MontgomeryModInt< mod >;

using T = modint;

// 遅延伝搬をするための作用素
struct Lazy {

  // 単位元
  Lazy() {}

  // 初期化
  Lazy(T v) {}

  // 遅延伝搬
  void propagate(const Lazy &p) {}
};

// Light-edge の情報
template< typename Lazy >
struct LInfo {
  T v, sum;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  LInfo() : sum(0) {}

  // 初期化
  LInfo(T v) : v(v) {}

  // l, r は Splay-tree の子 (原理上、各ノード区別はない)
  void update(const LInfo &l, const LInfo &r) {
    sum = l.sum + r.sum + v;
  }

  // 部分木への遅延伝搬
  void propagate(const Lazy &p) {}
};

// Heavy-edge の情報
template< typename LInfo, typename Lazy >
struct Info {
  T ans, pall, call;
  int len;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  Info() : ans(0), pall(0), call(0), len(0) {}

  // 初期化
  Info(T v) {}

  // 反転
  void toggle() { swap(pall, call); }

  // pが親, cがheavy-edgeで結ばれた子, lがそれ以外の子
  void update(const Info &p, const Info &c, const LInfo &l) {
    ans = l.sum * 2 + 1;
    pall = c.pall * 2 + ans;
    call = p.call * 2 + ans;
    len = p.len + c.len + 1;
  }

  // 親と light-edge で繋げる
  LInfo link() const { return LInfo(pall); }

  // 遅延伝搬
  void propagate(const Lazy &p) {}

  // light-edgeに対する遅延伝搬
  // pathとsubtreeの遅延伝搬が両方ある場合に実装する
  void propagate_light(const Lazy &p) {}
};

using LCT = SuperLinkCutTree< Info, LInfo, Lazy >;


int main() {
  int N, Q;
  cin >> N;
  LCT lct;
  vector< LCT::NP > vs(N);
  for(int i = 0; i < N; i++) {
    vs[i] = lct.alloc(Info< LInfo< Lazy >, Lazy >(0));
  }
  for(int i = 1; i < N; i++) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    lct.evert(vs[a]);
    lct.link(vs[a], vs[b]);
  }
  cin >> Q;
  while(Q--) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    auto x = lct.query_path(vs[a], vs[b]).ans;
    auto y = lct.query_path(vs[b], vs[a]).ans;
    cout << modint(2).pow(lct.get_info(vs[a]).len - 1) * x * y << "\n";
  }
}
accuracy: 0.09090909090909091
time_ms: 240
memory_kb: 14672
final_rank: 15

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

using namespace std;

/**
 * @brief 何でもできるLCT
 */
template< typename LInfo, typename Lazy >
struct SplayTree {
  struct Node {
    Node *l, *r, *p;
    LInfo info;
    Lazy lazy, lbuf;

    explicit Node(const LInfo &info) : info(info), l(nullptr), r(nullptr),
                                       p(nullptr), lazy(Lazy()), lbuf(Lazy()) {}
  };

  const LInfo e;

  SplayTree() : e(LInfo()) {}

  using NP = Node *;

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  const LInfo &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r));
  }

  NP get_right(NP t) {
    while(t->r) t = t->r;
    return t;
  }

  NP alloc(const LInfo &v) {
    auto t = new Node(v);
    update(t);
    return t;
  }

  void propagate(NP t, const Lazy &lazy) {
    t->info.propagate(lazy);
    t->lbuf.propagate(lazy);
    t->lazy.propagate(lazy);
  }

  void push(NP t) {
    if(t->l) propagate(t->l, t->lazy);
    if(t->r) propagate(t->r, t->lazy);
    t->lazy = Lazy();
  }

  void splay(NP t) {
    push(t);
    while(t->p) {
      NP q = t->p;
      if(!q->p) {
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP insert(NP t, const LInfo &v) {
    if(not t) {
      t = alloc(v);
      return t;
    } else {
      NP cur = get_right(t), z = alloc(v);
      splay(cur);
      z->p = cur;
      cur->r = z;
      update(cur);
      splay(z);
      return z;
    }
  }

  NP erase(NP t) {
    splay(t);
    NP x = t->l, y = t->r;
    delete t;
    if(not x) {
      t = y;
      if(t) t->p = nullptr;
    } else if(not y) {
      t = x;
      t->p = nullptr;
    } else {
      x->p = nullptr;
      t = get_right(x);
      splay(t);
      t->r = y;
      y->p = t;
      update(t);
    }
    return t;
  }
};

template< template< typename, typename > typename _Info,
    template< typename > typename _LInfo, typename Lazy >
struct SuperLinkCutTree {
  using LInfo = _LInfo< Lazy >;
  using Info = _Info< LInfo, Lazy >;

private:
  struct Node {
    Node *l, *r, *p;
    Info info;
    typename SplayTree< LInfo, Lazy >::Node *light, *belong;
    bool rev;
    Lazy hlazy, llazy;

    bool is_root() const {
      return not p or (p->l != this and p->r != this);
    }

    explicit Node(const Info &info)
        : info(info), l(nullptr), r(nullptr), p(nullptr), rev(false),
          light(nullptr), belong(nullptr), hlazy(Lazy()), llazy(Lazy()) {}
  };

public:
  using NP = Node *;
  SplayTree< LInfo, Lazy > splay_tree;

private:
  const Info e;

private:
  void toggle(NP t) {
    swap(t->l, t->r);
    t->info.toggle();
    t->rev ^= true;
  }

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void propagate_heavy(NP t, const Lazy &hlazy) {
    t->hlazy.propagate(hlazy);
    t->info.propagate(hlazy);
  }

  void propagate_light(NP t, const Lazy &llazy) {
    t->llazy.propagate(llazy);
    t->info.propagate_light(llazy);
  }

  void propagate_all(NP t, const Lazy &lazy) {
    propagate_heavy(t, lazy);
    propagate_light(t, lazy);
  }

public:
  SuperLinkCutTree() : e{Info()}, splay_tree{} {}

  void push(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
    {
      if(t->l) {
        propagate_heavy(t->l, t->hlazy);
        propagate_light(t->l, t->llazy);
      }
      if(t->r) {
        propagate_heavy(t->r, t->hlazy);
        propagate_light(t->r, t->llazy);
      }
      if(t->light) {
        splay_tree.propagate(t->light, t->llazy);
      }
      t->hlazy = Lazy();
      t->llazy = Lazy();
    }
  }

  void push_rev(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
  }

  const Info &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r), splay_tree.get_info(t->light));
  }

  void splay(NP t) {
    push(t);
    {
      NP rot = t;
      while(not rot->is_root()) rot = rot->p;
      t->belong = rot->belong;
      if(t != rot) rot->belong = nullptr;
    }
    while(not t->is_root()) {
      NP q = t->p;
      if(q->is_root()) {
        push_rev(q), push_rev(t);
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        push_rev(r), push_rev(q), push_rev(t);
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP expose(NP t) {
    NP rp = nullptr;
    for(NP cur = t; cur; cur = cur->p) {
      splay(cur);
      if(cur->r) {
        cur->light = splay_tree.insert(cur->light, cur->r->info.link());
        cur->r->belong = cur->light;
      }
      cur->r = rp;
      if(cur->r) {
        splay_tree.splay(cur->r->belong);
        propagate_all(cur->r, cur->r->belong->lbuf);
        push(cur->r);
        cur->light = splay_tree.erase(cur->r->belong);
      }
      update(cur);
      rp = cur;
    }
    splay(t);
    return rp;
  }

  void link(NP child, NP parent) {
    expose(parent);
    expose(child);
    child->p = parent;
    parent->r = child;
    update(parent);
  }

  void cut(NP child) {
    expose(child);
    NP parent = child->l;
    child->l = nullptr;
    parent->p = nullptr;
    update(child);
  }

  void evert(NP t) {
    expose(t);
    toggle(t);
    push(t);
  }

  NP alloc(const Info &info) {
    NP t = new Node(info);
    update(t);
    return t;
  }

  bool is_connected(NP u, NP v) {
    expose(u), expose(v);
    return u == v or u->p;
  }

  vector< NP > build(vector< Info > &vs) {
    vector< NP > nodes(vs.size());
    for(int i = 0; i < (int) vs.size(); i++) {
      nodes[i] = alloc(vs[i]);
    }
    return nodes;
  }

  NP lca(NP u, NP v) {
    if(not is_connected(u, v)) return nullptr;
    expose(u);
    return expose(v);
  }

  void set_key(NP t, const Info &v) {
    expose(t);
    t->info = move(v);
    update(t);
  }

  void set_propagate_path(NP t, const Lazy &lazy) {
    expose(t);
    propagate_heavy(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_path(NP u, NP v, const Lazy &lazy) {
    evert(u);
    set_propagate_path(v, lazy);
  }

  void set_propagate_all(NP t, const Lazy &lazy) {
    expose(t);
    propagate_all(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_subtree(NP t, const Lazy &lazy) {
    expose(t);
    NP l = t->l;
    t->l = nullptr;
    propagate_all(t, lazy);
    push(t);
    t->l = l;
    update(t);
  }

  const Info &query(NP u) {
    expose(u);
    return get_info(u);
  }

  const Info &query_path(NP u, NP v) {
    evert(u);
    expose(v);
    return get_info(v);
  }

  Info query_subtree(NP u) {
    expose(u);
    NP l = u->l;
    u->l = nullptr;
    update(u);
    auto ret = u->info;
    u->l = l;
    update(u);
    return ret;
  }
};


/**
 * @brief Montgomery ModInt
 */
template< uint32_t mod, bool fast = false >
struct MontgomeryModInt {
  using mint = MontgomeryModInt;
  using i32 = int32_t;
  using i64 = int64_t;
  using u32 = uint32_t;
  using u64 = uint64_t;

  static constexpr u32 get_r() {
    u32 ret = mod;
    for(i32 i = 0; i < 4; i++) ret *= 2 - mod * ret;
    return ret;
  }

  static constexpr u32 r = get_r();
  static constexpr u32 n2 = -u64(mod) % mod;

  static_assert(r * mod == 1, "invalid, r * mod != 1");
  static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30");
  static_assert((mod & 1) == 1, "invalid, mod % 2 == 0");

  u32 x;

  MontgomeryModInt() : x{} {}

  MontgomeryModInt(const i64 &a)
      : x(reduce(u64(fast ? a : (a % mod + mod)) * n2)) {}

  static constexpr u32 reduce(const u64 &b) {
    return u32(b >> 32) + mod - u32((u64(u32(b) * r) * mod) >> 32);
  }

  mint &operator+=(const mint &p) {
    if(i32(x += p.x - 2 * mod) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator-=(const mint &p) {
    if(i32(x -= p.x) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator*=(const mint &p) {
    x = reduce(u64(x) * p.x);
    return *this;
  }

  mint &operator/=(const mint &p) {
    *this *= p.inverse();
    return *this;
  }

  mint operator-() const { return mint() - *this; }

  mint operator+(const mint &p) const { return mint(*this) += p; }

  mint operator-(const mint &p) const { return mint(*this) -= p; }

  mint operator*(const mint &p) const { return mint(*this) *= p; }

  mint operator/(const mint &p) const { return mint(*this) /= p; }

  bool operator==(const mint &p) const { return (x >= mod ? x - mod : x) == (p.x >= mod ? p.x - mod : p.x); }

  bool operator!=(const mint &p) const { return (x >= mod ? x - mod : x) != (p.x >= mod ? p.x - mod : p.x); }

  u32 get() const {
    u32 ret = reduce(x);
    return ret >= mod ? ret - mod : ret;
  }

  mint pow(u64 n) const {
    mint ret(1), mul(*this);
    while(n > 0) {
      if(n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }

  mint inverse() const {
    return pow(mod - 2);
  }

  friend ostream &operator<<(ostream &os, const mint &p) {
    return os << p.get();
  }

  friend istream &operator>>(istream &is, mint &a) {
    i64 t;
    is >> t;
    a = mint(t);
    return is;
  }

  static u32 get_mod() { return mod; }
};

constexpr int mod = 998244353;
using modint = MontgomeryModInt< mod >;

using T = modint;

// 遅延伝搬をするための作用素
struct Lazy {

  // 単位元
  Lazy() {}

  // 初期化
  Lazy(T v) {}

  // 遅延伝搬
  void propagate(const Lazy &p) {}
};

// Light-edge の情報
template< typename Lazy >
struct LInfo {
  T v, sum;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  LInfo() : sum(0) {}

  // 初期化
  LInfo(T v) : v(v) {}

  // l, r は Splay-tree の子 (原理上、各ノード区別はない)
  void update(const LInfo &l, const LInfo &r) {
    sum = l.sum + r.sum + v;
  }

  // 部分木への遅延伝搬
  void propagate(const Lazy &p) {}
};

// Heavy-edge の情報
template< typename LInfo, typename Lazy >
struct Info {
  T ans, pall, call;
  int len;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  Info() : ans(0), pall(0), call(0), len(0) {}

  // 初期化
  Info(T v) {}

  // 反転
  void toggle() { swap(pall, call); }

  // pが親, cがheavy-edgeで結ばれた子, lがそれ以外の子
  void update(const Info &p, const Info &c, const LInfo &l) {
    ans = l.sum * 2 + 1;
    pall = c.pall * 2 + ans;
    call = p.call * 2 + ans;
    len = p.len + c.len + 1;
  }

  // 親と light-edge で繋げる
  LInfo link() const { return LInfo(pall); }

  // 遅延伝搬
  void propagate(const Lazy &p) {}

  // light-edgeに対する遅延伝搬
  // pathとsubtreeの遅延伝搬が両方ある場合に実装する
  void propagate_light(const Lazy &p) {}
};

using LCT = SuperLinkCutTree< Info, LInfo, Lazy >;


int main() {
  int N, Q;
  cin >> N;
  LCT lct;
  vector< LCT::NP > vs(N);
  for(int i = 0; i < N; i++) {
    vs[i] = lct.alloc(Info< LInfo< Lazy >, Lazy >(0));
  }
  for(int i = 1; i < N; i++) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    lct.evert(vs[a]);
    lct.link(vs[a], vs[b]);
  }
  cin >> Q;
  while(Q--) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    lct.evert(vs[a]);
    lct.expose(vs[b]);
    auto x = lct.query_path(vs[a], vs[b]).ans;
    auto y = lct.query_path(vs[b], vs[a]).ans;
    cout << modint(2).pow(lct.get_info(vs[a]).len - 1) * x * y << "\n";
  }
}
accuracy: 0.09090909090909091
time_ms: 250
memory_kb: 14436
final_rank: 16

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

using namespace std;

/**
 * @brief 何でもできるLCT
 */
template< typename LInfo, typename Lazy >
struct SplayTree {
  struct Node {
    Node *l, *r, *p;
    LInfo info;
    Lazy lazy, lbuf;

    explicit Node(const LInfo &info) : info(info), l(nullptr), r(nullptr),
                                       p(nullptr), lazy(Lazy()), lbuf(Lazy()) {}
  };

  const LInfo e;

  SplayTree() : e(LInfo()) {}

  using NP = Node *;

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  const LInfo &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r));
  }

  NP get_right(NP t) {
    while(t->r) t = t->r;
    return t;
  }

  NP alloc(const LInfo &v) {
    auto t = new Node(v);
    update(t);
    return t;
  }

  void propagate(NP t, const Lazy &lazy) {
    t->info.propagate(lazy);
    t->lbuf.propagate(lazy);
    t->lazy.propagate(lazy);
  }

  void push(NP t) {
    if(t->l) propagate(t->l, t->lazy);
    if(t->r) propagate(t->r, t->lazy);
    t->lazy = Lazy();
  }

  void splay(NP t) {
    push(t);
    while(t->p) {
      NP q = t->p;
      if(!q->p) {
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP insert(NP t, const LInfo &v) {
    if(not t) {
      t = alloc(v);
      return t;
    } else {
      NP cur = get_right(t), z = alloc(v);
      splay(cur);
      z->p = cur;
      cur->r = z;
      update(cur);
      splay(z);
      return z;
    }
  }

  NP erase(NP t) {
    splay(t);
    NP x = t->l, y = t->r;
    delete t;
    if(not x) {
      t = y;
      if(t) t->p = nullptr;
    } else if(not y) {
      t = x;
      t->p = nullptr;
    } else {
      x->p = nullptr;
      t = get_right(x);
      splay(t);
      t->r = y;
      y->p = t;
      update(t);
    }
    return t;
  }
};

template< template< typename, typename > typename _Info,
    template< typename > typename _LInfo, typename Lazy >
struct SuperLinkCutTree {
  using LInfo = _LInfo< Lazy >;
  using Info = _Info< LInfo, Lazy >;

private:
  struct Node {
    Node *l, *r, *p;
    Info info;
    typename SplayTree< LInfo, Lazy >::Node *light, *belong;
    bool rev;
    Lazy hlazy, llazy;

    bool is_root() const {
      return not p or (p->l != this and p->r != this);
    }

    explicit Node(const Info &info)
        : info(info), l(nullptr), r(nullptr), p(nullptr), rev(false),
          light(nullptr), belong(nullptr), hlazy(Lazy()), llazy(Lazy()) {}
  };

public:
  using NP = Node *;
  SplayTree< LInfo, Lazy > splay_tree;

private:
  const Info e;

private:
  void toggle(NP t) {
    swap(t->l, t->r);
    t->info.toggle();
    t->rev ^= true;
  }

  void rotr(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->l = t->r)) t->r->p = x;
    t->r = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void rotl(NP t) {
    NP x = t->p, y = x->p;
    push(x), push(t);
    if((x->r = t->l)) t->l->p = x;
    t->l = x, x->p = t;
    update(x), update(t);
    if((t->p = y)) {
      if(y->l == x) y->l = t;
      if(y->r == x) y->r = t;
    }
  }

  void propagate_heavy(NP t, const Lazy &hlazy) {
    t->hlazy.propagate(hlazy);
    t->info.propagate(hlazy);
  }

  void propagate_light(NP t, const Lazy &llazy) {
    t->llazy.propagate(llazy);
    t->info.propagate_light(llazy);
  }

  void propagate_all(NP t, const Lazy &lazy) {
    propagate_heavy(t, lazy);
    propagate_light(t, lazy);
  }

public:
  SuperLinkCutTree() : e{Info()}, splay_tree{} {}

  void push(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
    {
      if(t->l) {
        propagate_heavy(t->l, t->hlazy);
        propagate_light(t->l, t->llazy);
      }
      if(t->r) {
        propagate_heavy(t->r, t->hlazy);
        propagate_light(t->r, t->llazy);
      }
      if(t->light) {
        splay_tree.propagate(t->light, t->llazy);
      }
      t->hlazy = Lazy();
      t->llazy = Lazy();
    }
  }

  void push_rev(NP t) {
    if(t->rev) {
      if(t->l) toggle(t->l);
      if(t->r) toggle(t->r);
      t->rev = false;
    }
  }

  const Info &get_info(NP t) {
    return t ? t->info : e;
  }

  void update(NP t) {
    t->info.update(get_info(t->l), get_info(t->r), splay_tree.get_info(t->light));
  }

  void splay(NP t) {
    push(t);
    {
      NP rot = t;
      while(not rot->is_root()) rot = rot->p;
      t->belong = rot->belong;
      if(t != rot) rot->belong = nullptr;
    }
    while(not t->is_root()) {
      NP q = t->p;
      if(q->is_root()) {
        push_rev(q), push_rev(t);
        if(q->l == t) rotr(t);
        else rotl(t);
      } else {
        NP r = q->p;
        push_rev(r), push_rev(q), push_rev(t);
        if(r->l == q) {
          if(q->l == t) rotr(q), rotr(t);
          else rotl(t), rotr(t);
        } else {
          if(q->r == t) rotl(q), rotl(t);
          else rotr(t), rotl(t);
        }
      }
    }
  }

  NP expose(NP t) {
    NP rp = nullptr;
    for(NP cur = t; cur; cur = cur->p) {
      splay(cur);
      if(cur->r) {
        cur->light = splay_tree.insert(cur->light, cur->r->info.link());
        cur->r->belong = cur->light;
      }
      cur->r = rp;
      if(cur->r) {
        splay_tree.splay(cur->r->belong);
        propagate_all(cur->r, cur->r->belong->lbuf);
        push(cur->r);
        cur->light = splay_tree.erase(cur->r->belong);
      }
      update(cur);
      rp = cur;
    }
    splay(t);
    return rp;
  }

  void link(NP child, NP parent) {
    expose(parent);
    expose(child);
    child->p = parent;
    parent->r = child;
    update(parent);
  }

  void cut(NP child) {
    expose(child);
    NP parent = child->l;
    child->l = nullptr;
    parent->p = nullptr;
    update(child);
  }

  void evert(NP t) {
    expose(t);
    toggle(t);
    push(t);
  }

  NP alloc(const Info &info) {
    NP t = new Node(info);
    update(t);
    return t;
  }

  bool is_connected(NP u, NP v) {
    expose(u), expose(v);
    return u == v or u->p;
  }

  vector< NP > build(vector< Info > &vs) {
    vector< NP > nodes(vs.size());
    for(int i = 0; i < (int) vs.size(); i++) {
      nodes[i] = alloc(vs[i]);
    }
    return nodes;
  }

  NP lca(NP u, NP v) {
    if(not is_connected(u, v)) return nullptr;
    expose(u);
    return expose(v);
  }

  void set_key(NP t, const Info &v) {
    expose(t);
    t->info = move(v);
    update(t);
  }

  void set_propagate_path(NP t, const Lazy &lazy) {
    expose(t);
    propagate_heavy(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_path(NP u, NP v, const Lazy &lazy) {
    evert(u);
    set_propagate_path(v, lazy);
  }

  void set_propagate_all(NP t, const Lazy &lazy) {
    expose(t);
    propagate_all(t, lazy);
    push(t);
    update(t);
  }

  void set_propagate_subtree(NP t, const Lazy &lazy) {
    expose(t);
    NP l = t->l;
    t->l = nullptr;
    propagate_all(t, lazy);
    push(t);
    t->l = l;
    update(t);
  }

  const Info &query(NP u) {
    expose(u);
    return get_info(u);
  }

  const Info &query_path(NP u, NP v) {
    evert(u);
    expose(v);
    return get_info(v);
  }

  Info query_subtree(NP u) {
    expose(u);
    NP l = u->l;
    u->l = nullptr;
    update(u);
    auto ret = u->info;
    u->l = l;
    update(u);
    return ret;
  }
};


/**
 * @brief Montgomery ModInt
 */
template< uint32_t mod, bool fast = false >
struct MontgomeryModInt {
  using mint = MontgomeryModInt;
  using i32 = int32_t;
  using i64 = int64_t;
  using u32 = uint32_t;
  using u64 = uint64_t;

  static constexpr u32 get_r() {
    u32 ret = mod;
    for(i32 i = 0; i < 4; i++) ret *= 2 - mod * ret;
    return ret;
  }

  static constexpr u32 r = get_r();
  static constexpr u32 n2 = -u64(mod) % mod;

  static_assert(r * mod == 1, "invalid, r * mod != 1");
  static_assert(mod < (1 << 30), "invalid, mod >= 2 ^ 30");
  static_assert((mod & 1) == 1, "invalid, mod % 2 == 0");

  u32 x;

  MontgomeryModInt() : x{} {}

  MontgomeryModInt(const i64 &a)
      : x(reduce(u64(fast ? a : (a % mod + mod)) * n2)) {}

  static constexpr u32 reduce(const u64 &b) {
    return u32(b >> 32) + mod - u32((u64(u32(b) * r) * mod) >> 32);
  }

  mint &operator+=(const mint &p) {
    if(i32(x += p.x - 2 * mod) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator-=(const mint &p) {
    if(i32(x -= p.x) < 0) x += 2 * mod;
    return *this;
  }

  mint &operator*=(const mint &p) {
    x = reduce(u64(x) * p.x);
    return *this;
  }

  mint &operator/=(const mint &p) {
    *this *= p.inverse();
    return *this;
  }

  mint operator-() const { return mint() - *this; }

  mint operator+(const mint &p) const { return mint(*this) += p; }

  mint operator-(const mint &p) const { return mint(*this) -= p; }

  mint operator*(const mint &p) const { return mint(*this) *= p; }

  mint operator/(const mint &p) const { return mint(*this) /= p; }

  bool operator==(const mint &p) const { return (x >= mod ? x - mod : x) == (p.x >= mod ? p.x - mod : p.x); }

  bool operator!=(const mint &p) const { return (x >= mod ? x - mod : x) != (p.x >= mod ? p.x - mod : p.x); }

  u32 get() const {
    u32 ret = reduce(x);
    return ret >= mod ? ret - mod : ret;
  }

  mint pow(u64 n) const {
    mint ret(1), mul(*this);
    while(n > 0) {
      if(n & 1) ret *= mul;
      mul *= mul;
      n >>= 1;
    }
    return ret;
  }

  mint inverse() const {
    return pow(mod - 2);
  }

  friend ostream &operator<<(ostream &os, const mint &p) {
    return os << p.get();
  }

  friend istream &operator>>(istream &is, mint &a) {
    i64 t;
    is >> t;
    a = mint(t);
    return is;
  }

  static u32 get_mod() { return mod; }
};

constexpr int mod = 998244353;
using modint = MontgomeryModInt< mod >;

using T = modint;

// 遅延伝搬をするための作用素
struct Lazy {

  // 単位元
  Lazy() {}

  // 初期化
  Lazy(T v) {}

  // 遅延伝搬
  void propagate(const Lazy &p) {}
};

// Light-edge の情報
template< typename Lazy >
struct LInfo {
  T v, sum;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  LInfo() : sum(0) {}

  // 初期化
  LInfo(T v) : v(v) {}

  // l, r は Splay-tree の子 (原理上、各ノード区別はない)
  void update(const LInfo &l, const LInfo &r) {
    sum = l.sum + r.sum + v;
  }

  // 部分木への遅延伝搬
  void propagate(const Lazy &p) {}
};

// Heavy-edge の情報
template< typename LInfo, typename Lazy >
struct Info {
  T ans, pall, call;
  int len;

  // 単位元(キーの値はアクセスしないので未初期化でもよい
  Info() : ans(0), pall(0), call(0), len(0) {}

  // 初期化
  Info(T v) {}

  // 反転
  void toggle() {}

  // pが親, cがheavy-edgeで結ばれた子, lがそれ以外の子
  void update(const Info &p, const Info &c, const LInfo &l) {
    ans = l.sum * 2 + 1;
    pall = c.pall * 2 + ans;
    call = p.call * 2 + ans;
    len = p.len + c.len + 1;
  }

  // 親と light-edge で繋げる
  LInfo link() const { return LInfo(call); }

  // 遅延伝搬
  void propagate(const Lazy &p) {}

  // light-edgeに対する遅延伝搬
  // pathとsubtreeの遅延伝搬が両方ある場合に実装する
  void propagate_light(const Lazy &p) {}
};

using LCT = SuperLinkCutTree< Info, LInfo, Lazy >;


int main() {
  int N, Q;
  cin >> N;
  LCT lct;
  vector< LCT::NP > vs(N);
  for(int i = 0; i < N; i++) {
    vs[i] = lct.alloc(Info< LInfo< Lazy >, Lazy >(0));
  }
  for(int i = 1; i < N; i++) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    lct.evert(vs[a]);
    lct.link(vs[a], vs[b]);
  }
  cin >> Q;
  while(Q--) {
    int a, b;
    cin >> a >> b;
    --a, --b;
    lct.evert(vs[a]);
    lct.expose(vs[b]);
    auto x = lct.query_path(vs[a], vs[b]).ans;
    auto y = lct.query_path(vs[b], vs[a]).ans;
    cout << modint(2).pow(lct.get_info(vs[a]).len - 1) * x * y << "\n";
  }
}
accuracy: 0.09090909090909091
time_ms: 250
memory_kb: 14736
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T &a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T &a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr ll llINF = 3000000000000000000;
constexpr int mod = 998244353;
const ll half = (mod + 1) / 2;
constexpr double eps = 1e-10;
vector<int> prime_list(int n) {
    vector<int> v(n + 1), res;
    for (int i = n; i >= 2; i--) {
        for (int j = i; j <= n; j += i) v[j] = i;
    }
    for (int i = 2; i <= n; i++) {
        if (v[i] == i) res.push_back(i);
    }
    return res;
}
vector<int> next_divisor(int n) {
    vector<int> v(n + 1);
    for (int i = n; i >= 2; i--) {
        for (int j = i; j <= n; j += i) v[j] = i;
    }
    return v;
}
ll modpow(ll a, ll b, ll m = mod) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
vector<ll> inv, fact, factinv;
void init_fact(int n) {
    inv.resize(n + 1);
    fact.resize(n + 1);
    factinv.resize(n + 1);
    inv[0] = 0;
    inv[1] = 1;
    fact[0] = 1;
    factinv[0] = 1;
    for (ll i = 1; i <= n; i++) {
        if (i >= 2) inv[i] = mod - ((mod / i) * inv[mod % i] % mod);
        fact[i] = (fact[i - 1] * i) % mod;
        factinv[i] = factinv[i - 1] * inv[i] % mod;
    }
}
ll modinv(ll a, ll m = mod) {
    // gcd(a,m) must be 1
    ll b = m, u = 1, v = 0;
    while (b) {
        ll t = a / b;
        a -= t * b;
        swap(a, b);
        u -= t * v;
        swap(u, v);
    }
    u %= m;
    if (u < 0) u += m;
    return u;
}
ll comb(int a, int b) {
    if (a < b || a < 0 || b < 0) return 0;
    return fact[a] * factinv[a - b] % mod * factinv[b] % mod;
}

template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
struct lca_tree {
    int n;
    vector<vector<int>> g, par;
    vector<int> depth;
    lca_tree(vector<vector<int>> g, int root = 0) : n(g.size()), g(g), depth(vector<int>(n, n)), par(vector(log2(n) + 2, vector<int>(n, -1))) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (int i : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < (int)par.size() - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = 0; k < (int)par.size(); k++) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = par.size() - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
};
void solve() {
    int n;
    cin >> n;
    vector<vector<int>> g(n);
    rep(i, n - 1) {
        int a, b;
        cin >> a >> b;
        a--, b--;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    vector<ll> dp(n), sdp(n);
    function<void(int, int)> dfs = [&](int v, int par) {
        for (int i : g[v]) {
            if (i == par) continue;
            dfs(i, v);
            dp[v] += dp[i];
        }
        dp[v] = (dp[v] * 2 + 1) % mod;
    };
    dfs(0, -1);
    function<void(int, int)> dfs2 = [&](int v, int par) {
        if (v != 0) {
            sdp[v] = sdp[par] * 2 + (dp[par] + mod + mod - dp[v] * 2) * 2;
            sdp[v] %= mod;
        }
        for (int i : g[v]) {
            if (i == par) continue;
            dfs2(i, v);
        }
    };
    dfs2(0, -1);
    lca_tree lt(g);
    int q;
    cin >> q;
    rep(i, q) {
        int x, y;
        cin >> x >> y;
        x--, y--;
        int l = lt.query(x, y);
        if (x != l && y != l) {
            cout << dp[x] * dp[y] % mod * modpow(2, lt.depth[x] + lt.depth[y] - 2 * lt.depth[l]) % mod << endl;
        } else {
            if (y == l) swap(x, y);
            // x==l
            int d = lt.depth[y] - lt.depth[x] - 1;
            int cur = y;
            for (int i = 17; i >= 0; i--) {
                if ((d >> i) & 1) cur = lt.par[i][cur];
            }
            cout << sdp[cur] * dp[y] % mod * modpow(2, d) % mod << endl;
        }
    }
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    /*int t;
    cin>>t;
    while(t--)*/
    solve();
}
accuracy: 1
time_ms: 90
memory_kb: 37988
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
template <class T, class U>
inline bool chmax(T &a, U b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T, class U>
inline bool chmin(T &a, U b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
constexpr int INF = 1000000000;
constexpr ll llINF = 3000000000000000000;
constexpr int mod = 998244353;
const ll half = (mod + 1) / 2;
constexpr double eps = 1e-10;
vector<int> prime_list(int n) {
    vector<int> v(n + 1), res;
    for (int i = n; i >= 2; i--) {
        for (int j = i; j <= n; j += i) v[j] = i;
    }
    for (int i = 2; i <= n; i++) {
        if (v[i] == i) res.push_back(i);
    }
    return res;
}
vector<int> next_divisor(int n) {
    vector<int> v(n + 1);
    for (int i = n; i >= 2; i--) {
        for (int j = i; j <= n; j += i) v[j] = i;
    }
    return v;
}
ll modpow(ll a, ll b, ll m = mod) {
    ll res = 1;
    while (b) {
        if (b & 1) {
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
vector<ll> inv, fact, factinv;
void init_fact(int n) {
    inv.resize(n + 1);
    fact.resize(n + 1);
    factinv.resize(n + 1);
    inv[0] = 0;
    inv[1] = 1;
    fact[0] = 1;
    factinv[0] = 1;
    for (ll i = 1; i <= n; i++) {
        if (i >= 2) inv[i] = mod - ((mod / i) * inv[mod % i] % mod);
        fact[i] = (fact[i - 1] * i) % mod;
        factinv[i] = factinv[i - 1] * inv[i] % mod;
    }
}
ll modinv(ll a, ll m = mod) {
    // gcd(a,m) must be 1
    ll b = m, u = 1, v = 0;
    while (b) {
        ll t = a / b;
        a -= t * b;
        swap(a, b);
        u -= t * v;
        swap(u, v);
    }
    u %= m;
    if (u < 0) u += m;
    return u;
}
ll comb(int a, int b) {
    if (a < b || a < 0 || b < 0) return 0;
    return fact[a] * factinv[a - b] % mod * factinv[b] % mod;
}

template <class S, S (*op)(S, S), S (*e)()>
struct dual_segtree {
    int sz = 1, log = 0;
    vector<S> lz;
    dual_segtree(vector<S> a) {
        int n = a.size();
        while (sz < n) {
            sz <<= 1;
            log++;
        }
        lz.assign(sz << 1, e());
        for (int i = 0; i < n; i++) lz[i + sz] = a[i];
    }
    void push(int k) {
        int b = __builtin_ctz(k);
        for (int d = log; d > b; d--) {
            lz[k >> d << 1] = op(lz[k >> d << 1], lz[k >> d]);
            lz[k >> d << 1 | 1] = op(lz[k >> d << 1 | 1], lz[k >> d]);
            lz[k >> d] = e();
        }
    }
    void apply(int l, int r, S x) {
        l += sz, r += sz;
        push(l);
        push(r);
        while (l < r) {
            if (l & 1) {
                lz[l] = op(lz[l], x);
                l++;
            }
            if (r & 1) {
                r--;
                lz[r] = op(lz[r], x);
            }
            l >>= 1, r >>= 1;
        }
    }
    S get(int k) {
        k += sz;
        S res = e();
        while (k) {
            res = op(res, lz[k]);
            k >>= 1;
        }
        return res;
    }
};
struct lca_tree {
    int n;
    vector<vector<int>> g, par;
    vector<int> depth;
    lca_tree(vector<vector<int>> g, int root = 0) : n(g.size()), g(g), depth(vector<int>(n, n)), par(vector(log2(n) + 2, vector<int>(n, -1))) {
        queue<int> que;
        depth[root] = 0;
        que.push(root);
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (int i : g[p]) {
                if (depth[i] > depth[p] + 1) {
                    depth[i] = depth[p] + 1;
                    par[0][i] = p;
                    que.push(i);
                }
            }
        }
        for (int k = 0; k < (int)par.size() - 1; k++) {
            for (int i = 0; i < n; i++) {
                if (par[k][i] == -1)
                    par[k + 1][i] = -1;
                else
                    par[k + 1][i] = par[k][par[k][i]];
            }
        }
    }
    int query(int u, int v) {
        if (depth[u] > depth[v]) swap(u, v);
        for (int k = 0; k < (int)par.size(); k++) {
            if (((depth[v] - depth[u]) >> k) & 1) v = par[k][v];
            if (u == v) return u;
        }
        for (int k = par.size() - 1; k >= 0; k--) {
            if (par[k][u] != par[k][v]) {
                u = par[k][u];
                v = par[k][v];
            }
        }
        return par[0][u];
    }
};
void solve() {
    int n;
    cin >> n;
    vector<vector<int>> g(n);
    rep(i, n - 1) {
        int a, b;
        cin >> a >> b;
        a--, b--;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    vector<ll> dp(n), sdp(n);
    function<void(int, int)> dfs = [&](int v, int par) {
        for (int i : g[v]) {
            if (i == par) continue;
            dfs(i, v);
            dp[v] += dp[i];
        }
        dp[v] = (dp[v] * 2 + 1) % mod;

        if (v != 0) {
            sdp[v] = sdp[par] * 2 + (dp[par] - dp[v] * 2) * 2;
        }
    };
    dfs(0, -1);
    function<void(int, int)> dfs2 = [&](int v, int par) {
        if (v != 0) {
            sdp[v] = sdp[par] * 2 + (dp[par] - dp[v] * 2) * 2;
        }
        for (int i : g[v]) {
            if (i == par) continue;
            dfs2(i, v);
        }
    };
    dfs2(0, -1);
    lca_tree lt(g);
    int q;
    cin >> q;
    rep(i, q) {
        int x, y;
        cin >> x >> y;
        x--, y--;
        int l = lt.query(x, y);
        if (x != l && y != l) {
            cout << dp[x] * dp[y] % mod * modpow(2, lt.depth[x] + lt.depth[y] - 2 * lt.depth[l]) % mod << endl;
        } else {
            if (y == l) swap(x, y);
            // x==l
            int d = lt.depth[y] - lt.depth[x] - 1;
            int cur = y;
            for (int i = 17; i >= 0; i--) {
                if ((d >> i) & 1) cur = lt.par[i][cur];
            }
            cout << sdp[cur] * dp[y] % mod * modpow(2, d) % mod << endl;
        }
    }
}
int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    /*int t;
    cin>>t;
    while(t--)*/
    solve();
}
accuracy: 0.09090909090909091
time_ms: 80
memory_kb: 28732
final_rank: 14

code_snippet: #pragma GCC ta g("avx2")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define DEBUG
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<char> vc;
typedef vector<string> vs;
typedef vector<bool> vb;
typedef vector<double> vd;
typedef pair<ll,ll> P;
typedef pair<int,int> pii;
typedef vector<P> vpl;
typedef tuple<ll,ll,ll> tapu;
#define rep(i,n) for(int i=0; i<(n); i++)
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
template<typename T1,typename T2> inline bool chmin(T1 &a,T2 b){
	if(a>b){
		a = b; return true;
	}
	else return false;
}
template<typename T1,typename T2> inline bool chmax(T1 &a,T2 b){
	if(a<b){
		a = b; return true;
	}
	else return false;
}
template<typename T> inline void print(T &a){
    int sz = a.size();
    for(auto itr = a.begin(); itr != a.end(); itr++){
		cout << *itr;
        sz--;
        if(sz) cout << " ";
	}
    cout << "\n";
}
template<typename T1,typename T2> inline void print2(T1 a, T2 b){
	cout << a << " " << b << "\n";
}
template<typename T1,typename T2,typename T3> inline void print3(T1 a, T2 b, T3 c){
	cout << a << " " << b << " " << c << "\n";
}
void mark() {cout << "#" << "\n";}
ll pcount(ll x) {return __builtin_popcountll(x);}
//#include <atcoder/convolution>
//using namespace atcoder;
const int inf = (1<<30)-1;
const ll linf = 1LL<<61;
const int MAX = 510000;
int dy[8] = {0,1,0,-1,1,-1,-1,1};
int dx[8] = {-1,0,1,0,1,-1,1,-1};
const double pi = acos(-1);
const double eps = 1e-9;
//const int mod = 1e9 + 7;
const int mod = 998244353;

template <int mod> struct ModInt {
    int x;

    ModInt() : x(0) {}

    ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

    ModInt &operator+=(const ModInt &p) {
        if((x += p.x) >= mod) x -= mod;
        return *this;
    }

    ModInt &operator-=(const ModInt &p) {
        if((x += mod - p.x) >= mod) x -= mod;
        return *this;
    }

    ModInt &operator*=(const ModInt &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }

    ModInt &operator/=(const ModInt &p) {
        *this *= p.inverse();
        return *this;
    }

    ModInt operator-() const { return ModInt(-x); }

    ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }

    ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }

    ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }

    ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }

    bool operator==(const ModInt &p) const { return x == p.x; }

    bool operator!=(const ModInt &p) const { return x != p.x; }

    ModInt inverse() const {
        int a = x, b = mod, u = 1, v = 0, t;
        while(b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return ModInt(u);
    }

    ModInt pow(int64_t n) const {
        ModInt ret(1), mul(x);
        while(n > 0) {
            if(n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend ostream &operator<<(ostream &os, const ModInt &p) {
        return os << p.x;
    }

    friend istream &operator>>(istream &is, ModInt &a) {
        int64_t t;
        is >> t;
        a = ModInt<mod>(t);
        return (is);
    }

    static int get_mod() { return mod; }
};

using mint = ModInt<mod>;

vvl G(101010);

struct LCA{
    ll logv = 0;
    ll root = 0;
    vvl parent;
    vl depth;

    LCA(ll n, ll r){
        root = r;
        depth.resize(n,0);
        while(1LL<<logv < n){
            logv++;
        }
        parent.resize(logv+1);
        rep(i,logv+1){
            parent[i].resize(n,-1);
        }
        dfs(root,-1,0);
        rep(i,logv){
            rep(j,n){
                if(parent[i][j] < 0) parent[i+1][j] = -1;
                else parent[i+1][j] = parent[i][parent[i][j]];
            }
        }
    }

    void dfs(ll u, ll p, ll d){
        parent[0][u] = p;
        depth[u] = d;
        for(auto v : G[u]){
            if(v != p) dfs(v,u,d+1);
        }
    }

    ll lca(ll u, ll v){
        if(depth[u] > depth[v]) swap(u,v);
        rep(i,logv){
            if((depth[v] - depth[u]) >> i & 1) v = parent[i][v];
        }
        if(u == v) return u;
        for(ll i=logv-1; i>=0; i--){
            if(parent[i][u] != parent[i][v]){
                u = parent[i][u];
                v = parent[i][v];
            }
        }
        return parent[0][u];
    }
};

int main(){
    int n; cin >> n;
    rep(i,n-1){
        int a,b; cin >> a >> b; a--; b--;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    LCA lc(n,0);
    int q; cin >> q;
    vector<mint> ans(q);
    vl d(q);
    vl x(q),y(q);
    rep(i,q){
        cin >> x[i] >> y[i]; x[i]--; y[i]--;
    }
    vector<mint> val(n,1),res(n);
    vl ch(n,1);
    {
        auto dfs = [&](auto &&dfs, int u, int p) -> void {
            for(auto v : G[u]){
                if(v == p) continue;
                dfs(dfs,v,u);
                val[u] += val[v] * 2;
                ch[u] += ch[v];
            }
        };
        dfs(dfs,0,-1);
    }
    {
        res[0] = val[0];
        auto dfs = [&](auto &&dfs, int u, int p) -> void {
            if(u != 0){
                res[u] = (res[p] - val[u] * 2) * 2 + val[u];
            }
            for(auto v : G[u]){
                if(v == p) continue;
                dfs(dfs,v,u);
            }
        };
        dfs(dfs,0,-1);
    }
    //debug(res,val);
    vector<vpl> qu(n);
    rep(i,q){
        int p = lc.lca(x[i],y[i]);
        if(p == x[i]) qu[y[i]].emplace_back(x[i],i), d[i] = lc.depth[y[i]];
        else if(p == y[i]) qu[x[i]].emplace_back(y[i],i), d[i] = lc.depth[x[i]];
        else ans[i] = val[x[i]] * val[y[i]] * mint(2).pow(lc.depth[x[i]] + lc.depth[y[i]] - 2*lc.depth[p]);
    }
    vector<set<pii>> mt(n);
    auto dfs = [&](auto &&dfs, int u, int p) -> void {
        for(auto v : G[u]){
            if(v == p) continue;
            dfs(dfs,v,u);
            auto itr = mt[v].lower_bound(P(u,-inf));
            while(itr != mt[v].end() && (*itr).first == u){
                P pa = *itr;
                //debug(pa.first,u,d[pa.second],lc.depth[u]);
                ans[pa.second] *= mint(2).pow(d[pa.second] - lc.depth[u]);
                ans[pa.second] *= (res[u] - val[v] * 2);
                itr = mt[v].erase(itr);
            }
            if(mt[u].size() < mt[v].size()){
                for(auto x : mt[u]) mt[v].emplace(x);
                swap(mt[u],mt[v]);
            }else{
                for(auto x : mt[v]) mt[u].emplace(x);
            }
        }
        for(auto x : qu[u]){
            ans[x.second] = val[u];
            mt[u].emplace(x);
        }
    };
    dfs(dfs,0,-1);
    rep(i,q) cout << ans[i] << "\n";
}
accuracy: 1
time_ms: 100
memory_kb: 53876
final_rank: 11

code_snippet: #line 1 "i.cpp"
/*	author:  Kite_kuma
	created: 2022.04.03 16:34:11 */

// #ifdef LOCAL
// #define _GLIBCXX_DEBUG
// #endif
#include <bits/stdc++.h>
using namespace std;

#pragma region macros

#define foa(s, v) for(auto &s : v)
#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()
#define popcnt(n) __builtin_popcountll((long long)n)

#define REPname(a, b, c, d, e, ...) e
#define rep(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int Counter_in_rep_macro = 0; Counter_in_rep_macro < (x); ++Counter_in_rep_macro)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))

#define DREPname(a, b, c, d, e, ...) e
#define drep(...) DREPname(__VA_ARGS__, DREP3, DREP2, DREP1)(__VA_ARGS__)
#define DREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define DREP2(i, l, r) for(int i = (r)-1; i >= (l); --i)
#define DREP3(i, l, r, c) for(int i = (r)-1; i >= (l); i -= (c))

#pragma endregion

#pragma region aliases

using ll = long long;
using ld = long double;
using ull = unsigned long long;
using vi = std::vector<int>;
using vvi = std::vector<std::vector<int>>;
using vvvi = std::vector<std::vector<std::vector<int>>>;
using vll = std::vector<ll>;
using vvll = std::vector<vll>;
using vvvll = std::vector<vvll>;
using pii = std::pair<int, int>;
using pll = std::pair<long long, long long>;
template <class T = ll>
using V = std::vector<T>;
template <class T = ll>
using VV = V<V<T>>;
template <class T = ll>
using VVV = V<V<V<T>>>;
template <class T = ll>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <class T = ll>
using pqdn = std::priority_queue<T>;

#pragma endregion

#pragma region constants

const int inf = 1e9;
const long long INF = 1e18;
const long double pi = acos(-1);
const char dl = '\n';
const char sp = ' ';
int dx[8] = {1, 0, -1, 0, 1, -1, -1, 1};
int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};

const int mod_1000000007 = 1000000007;
const int mod_998244353 = 998244353;

#pragma endregion

#pragma region basic_operation

template <class T0, class T1, class T2>
inline bool in_range(T0 x, T1 lef, T2 rig) {
	return ((lef <= x) && (x < rig));
}

template <class T>
inline bool chmin(T &a, T b) {
	if(a > b) {
		a = b;
		return true;
	}
	return false;
}
template <class T>
inline bool chmax(T &a, T b) {
	if(a < b) {
		a = b;
		return true;
	}
	return false;
}

void Yes(bool f = 1) { std::cout << (f ? "Yes" : "No") << '\n'; }
void No() { std::cout << "No\n"; }
void YES(bool f = 1) { std::cout << (f ? "YES" : "NO") << '\n'; }
void NO() { std::cout << "NO\n"; }

template <class T>
void drop(T answer) {
	std::cout << answer << '\n';
	exit(0);
}

void err(bool flag = true) {
	if(!flag) return;
	std::cout << -1 << '\n';
	exit(0);
}

template <class T>
void vout(std::vector<T> const &v, bool vertically = 0) {
	if(vertically) {
		for(auto const &a : v) {
			std::cout << a << '\n';
		}
		return;
	}
	for(auto it = v.begin(); it != v.end(); it++) {
		std::cout << (*it);
		if(it != v.end() - 1) {
			std::cout << ' ';
		}
	}
	std::cout << '\n';
	return;
}

inline void print() { std::cout << '\n'; }
template <class T>
inline void print(T x) {
	std::cout << x << '\n';
	return;
}
template <typename Head, typename... Tail>
void print(Head H, Tail... T) {
	std::cout << H << " ";
	print(T...);
}

template <class T>
void add(std::vector<T> &v, T val) {
	for(auto &a : v) a += val;
	return;
}

template <class T>
T dup(T a, T b) {
	assert(b != 0);
	return (a + b - 1) / b;
}

template <class T>
T greatest_lower_multiple(T x, T d) {
	if(d == 0) return 0;
	if(d < 0) d *= -1;
	T y = x % d;
	if(y < 0) y += d;
	return x - y;
}

template <class T>
T least_upper_multiple(T x, T d) {
	return -greatest_lower_multiple(-x, d);
}

long long POW(long long a, long long n) {
	long long res = 1;
	while(n > 0) {
		if(n & 1) res = res * a;
		a = a * a;
		n >>= 1;
	}
	return res;
}

long long modpow(long long a, long long n, long long mod) {	 // a^n mod
	assert(n >= 0 && mod);
	if(mod == 1) return 0LL;
	long long res = 1;
	a %= mod;
	while(n > 0) {
		if(n & 1) res = res * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res < 0 ? res + mod : res;
}

// return x which satisfies a * x % mod == gcd(a, mod)
// not (mod | a)
long long modinv(long long a, long long mod) {
	long long b = mod, u = 1, v = 0;
	while(b) {
		long long t = a / b;
		a -= t * b;
		std::swap(a, b);
		u -= t * v;
		std::swap(u, v);
	}
	u %= mod;
	if(u < 0) u += mod;
	return u;
}

template <class T>
int lb(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::lower_bound(a.begin(), a.end(), x));
}
template <class T>
int ub(const std::vector<T> &a, const T x) {
	return std::distance(a.begin(), std::upper_bound(a.begin(), a.end(), x));
}
template <class T>
void unq_sort(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<int> press(std::vector<T> &a) {
	auto vec = a;
	unq_sort(vec);
	std::vector<int> ret;
	for(auto &v : a) ret.push_back(lb(vec, v));
	return ret;
}

#pragma endregion

#pragma region input
#define VEC(type, name, size) \
	vector<type> name(size);  \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                     \
	vector<vector<type>> name(h, vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)        \
	string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHR(...)      \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DBL(...)        \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define TPL3(type0, type1, type2, name)   \
	std::tuple<type0, type1, type2> name; \
	scanner::INPUT(name);
#define TPL4(type0, type1, type2, type3, name)   \
	std::tuple<type0, type1, type2, type3> name; \
	scanner::INPUT(name);

namespace scanner {
template <class T>
void scan(T &a) {
	std::cin >> a;
}

template <class T, class L>
void scan(std::pair<T, L> &p) {
	scan(p.first);
	scan(p.second);
}

template <class T0, class T1, class T2>
void scan(std::tuple<T0, T1, T2> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	scan(t0);
	scan(t1);
	scan(t2);
	p = std::make_tuple(t0, t1, t2);
}

template <class T0, class T1, class T2, class T3>
void scan(std::tuple<T0, T1, T2, T3> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	T3 t3;
	scan(t0);
	scan(t1);
	scan(t2);
	scan(t3);
	p = std::make_tuple(t0, t1, t2, t3);
}

template <class T>
void scan(std::vector<T> &a) {
	for(auto &i : a) scan(i);
}

void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &... tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner

template <typename T1, typename T2>
std::istream &operator>>(std::istream &is, std::pair<T1, T2> &p) {
	is >> p.first >> p.second;
	return is;
}
#pragma endregion

#pragma region debug

#pragma region output
template <typename T1, typename T2>
std::ostream &std::operator<<(std::ostream &os, const std::pair<T1, T2> &p) {
	os << p.first << " " << p.second;
	return os;
}
template <class T>
std::ostream &std::operator<<(std::ostream &os, const std::vector<T> &v) {
	for(int i = 0; i < (int)v.size(); i++) {
		if(i) os << " ";
		os << v[i];
	}
	return os;
}
#pragma endregion

#pragma region view

namespace viewer {
void view(const long long e) {
	if(e == INF)
		std::cerr << "INF";
	else if(e == -INF)
		std::cerr << "-INF";
	else
		std::cerr << e;
}

void view(const int e) {
	if(e == inf)
		std::cerr << "inf";
	else if(e == -inf)
		std::cerr << "-inf";
	else
		std::cerr << e;
}

template <typename T>
void view(const T e) {
	std::cerr << e;
}

template <typename T, typename U>
void view(const std::pair<T, U> &p) {
	std::cerr << "(";
	view(p.first);
	std::cerr << ", ";
	view(p.second);
	std::cerr << ")";
}

template <class T0, class T1, class T2>
void view(const std::tuple<T0, T1, T2> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ")";
}

template <class T0, class T1, class T2, class T3>
void view(const std::tuple<T0, T1, T2, T3> &p) {
	std::cerr << "(";
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ", ";
	view(std::get<3>(p));
	std::cerr << ")";
}

template <typename T>
void view(const std::set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::unordered_set<T> &s) {
	if(s.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(auto &t : s) {
		view(t);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<T> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << "{ ";
	for(const auto &e : v) {
		view(e);
		std::cerr << ", ";
	}
	std::cerr << "\b\b }";
}

template <typename T>
void view(const std::vector<std::vector<T>> &vv) {
	std::cerr << "{\n";
	for(const auto &v : vv) {
		std::cerr << "\t";
		view(v);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v) {
	std::cerr << "{\n";
	for(const auto &c : v) {
		std::cerr << "\t(";
		view(c.first);
		std::cerr << ", ";
		view(c.second);
		std::cerr << ")\n";
	}
	std::cerr << "}";
}

template <class T0, class T1, class T2>
void view(const std::vector<std::tuple<T0, T1, T2>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <class T0, class T1, class T2, class T3>
void view(const std::vector<std::tuple<T0, T1, T2, T3>> &v) {
	if(v.empty()) {
		std::cerr << "{ }";
		return;
	}
	std::cerr << '{';
	for(const auto &t : v) {
		std::cerr << "\n\t";
		view(t);
		std::cerr << ",";
	}
	std::cerr << "\n}";
}

template <typename T, typename U>
void view(const std::map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m) {
	std::cerr << "{\n";
	for(const auto &t : m) {
		std::cerr << "\t[";
		view(t.first);
		std::cerr << "] : ";
		view(t.second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}
}  // namespace viewer

#pragma endregion

// when debugging : g++ foo.cpp -DLOCAL
#ifdef LOCAL
void debug_out() {}
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) {
	viewer::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "\b\b]\n";                                   \
	} while(0)
#define dump(x)                                      \
	do {                                             \
		std::cerr << __LINE__ << " " << #x << " : "; \
		viewer::view(x);                             \
		std::cerr << '\n';                           \
	} while(0)

#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif

#pragma endregion

#pragma region modint

const int mod = mod_998244353;

template <int mod>
struct ModInt {
	int x;
	ModInt() : x(0) {}
	ModInt(long long y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
	ModInt &operator+=(const ModInt &p) {
		if((x += p.x) >= mod) x -= mod;
		return *this;
	}
	ModInt &operator-=(const ModInt &p) {
		if((x += mod - p.x) >= mod) x -= mod;
		return *this;
	}
	ModInt &operator*=(const ModInt &p) {
		x = (int)(1LL * x * p.x % mod);
		return *this;
	}
	ModInt &operator/=(const ModInt &p) {
		*this *= p.inverse();
		return *this;
	}
	ModInt operator-() const { return ModInt(-x); }
	ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }
	ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }
	ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }
	ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }
	bool operator==(const ModInt &p) const { return x == p.x; }
	bool operator!=(const ModInt &p) const { return x != p.x; }
	ModInt inverse() const {
		int a = x, b = mod, u = 1, v = 0, t;
		while(b > 0) {
			t = a / b;
			swap(a -= t * b, b);
			swap(u -= t * v, v);
		}
		return ModInt(u);
	}
	ModInt pow(long long n) const {
		ModInt ret(1), mul(x);
		while(n > 0) {
			if(n & 1) ret *= mul;
			mul *= mul;
			n >>= 1;
		}
		return ret;
	}
	friend ostream &operator<<(ostream &os, const ModInt &p) { return os << p.x; }
	friend istream &operator>>(istream &is, ModInt &a) {
		long long t;
		is >> t;
		a = ModInt<mod>(t);
		return (is);
	}
	static int get_mod() { return mod; }
};
using mint = ModInt<mod>;

#pragma endregion

#line 8 "SPJ-Library/graph/graph.hpp"

#pragma region graph

template <class cost_type = long long>
class graph {
   public:
	struct edge {
	   public:
		int from, to;
		cost_type cost;
		int id;
		edge() = default;
		edge(int from_, int to_, cost_type cost_ = 1, int id_ = -1) : from(from_), to(to_), cost(cost_), id(id_) {}
		bool operator<(const edge &a) const { return cost < a.cost; }
		bool operator>(const edge &a) const { return cost > a.cost; }
		friend std::ostream &operator<<(std::ostream &s, const edge &a) {
			s << '(' << a.from << " -> " << a.to << "), cost: " << a.cost << ", id: " << a.id;
			return s;
		}
	};

   private:
	std::vector<std::vector<edge>> edges;
	int next_edge_id = 0;

   public:
	inline const std::vector<edge> &operator[](int k) const { return edges[k]; }
	inline std::vector<edge> &operator[](int k) { return edges[k]; }

	int size() const { return int(edges.size()); }
	void resize(const int n) { edges.resize(n); }
	int edge_count() const { return next_edge_id; }

	friend std::ostream &operator<<(std::ostream &s, const graph<cost_type> &g) {
		for(const auto &adj : g.edges)
			for(const auto &ed : adj) s << ed << '\n';
		return s;
	}

	graph() = default;
	graph(int n) : edges(n) {}
	graph(int n, int e, bool weight = 0, bool directed = 0, int idx = 1) : edges(n) { input(e, weight, directed, idx); }
	const cost_type INF = std::numeric_limits<cost_type>::max() / 3;

	void input(int e = -1, bool weight = false, bool directed = false, int idx = 1) {
		if(e == -1) e = size() - 1;
		while(e--) {
			int u, v;
			std::cin >> u >> v;
			cost_type cost = 1;
			if(weight) std::cin >> cost;
			add_edge(u, v, cost, directed, idx);
		}
	}

	inline int add_edge(int u, int v, cost_type cost = 1, bool directed = false, int idx = 0) {
		u -= idx, v -= idx;
		edges[u].emplace_back(u, v, cost, next_edge_id);
		if(!directed && u != v) edges[v].emplace_back(v, u, cost, next_edge_id);
		return next_edge_id++;
	}

	// Ο(V+E)
	std::vector<cost_type> bfs(int s) {
		std::vector<cost_type> dist(size(), INF);
		std::queue<int> que;
		dist[s] = 0;
		que.push(s);
		while(!que.empty()) {
			int v = que.front();
			que.pop();
			for(auto &e : edges[v]) {
				if(dist[e.to] != INF) continue;
				dist[e.to] = dist[v] + e.cost;
				que.push(e.to);
			}
		}
		return dist;
	}

	// Ο(V+E)
	// constraint: cost of each edge is zero or one
	std::vector<cost_type> zero_one_bfs(int s) {
		std::vector<cost_type> dist(size(), INF);
		std::deque<int> deq;
		dist[s] = 0;
		deq.push_back(s);
		while(!deq.empty()) {
			int v = deq.front();
			deq.pop_front();
			for(auto &e : edges[v]) {
				assert(0LL <= e.cost and e.cost < 2LL);
				if(e.cost and dist[e.to] > dist[v] + 1) {
					dist[e.to] = dist[v] + 1;
					deq.push_back(e.to);
				} else if(!e.cost and dist[e.to] > dist[v]) {
					dist[e.to] = dist[v];
					deq.push_front(e.to);
				}
			}
		}
		return dist;
	}

	// Ο((E+V) lg E)
	// unreachable: INF
	std::vector<cost_type> dijkstra(int s) {  // verified
		std::vector<cost_type> dist(size(), INF);
		const auto compare = [](const std::pair<cost_type, int> &a, const std::pair<cost_type, int> &b) { return a.first > b.first; };
		std::priority_queue<std::pair<cost_type, int>, std::vector<std::pair<cost_type, int>>, decltype(compare)> que{compare};
		dist[s] = 0;
		que.emplace(0, s);
		while(!que.empty()) {
			std::pair<cost_type, int> p = que.top();
			que.pop();
			int v = p.second;
			if(dist[v] < p.first) continue;
			for(auto &e : edges[v]) {
				if(dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					que.emplace(dist[e.to], e.to);
				}
			}
		}
		return dist;
	}

	// Ο(VE)
	// unreachable: INF
	// reachable via negative cycle: -INF
	std::vector<cost_type> bellman_ford(int s) {
		int n = size();
		std::vector<cost_type> res(n, INF);
		res[s] = 0;
		for(int loop = 0; loop < n - 1; loop++) {
			for(int v = 0; v < n; v++) {
				if(res[v] == INF) continue;
				for(auto &e : edges[v]) {
					res[e.to] = std::min(res[e.to], res[v] + e.cost);
				}
			}
		}
		std::queue<int> que;
		std::vector<int> chk(n);
		for(int v = 0; v < n; v++) {
			if(res[v] == INF) continue;
			for(auto &e : edges[v]) {
				if(res[e.to] > res[v] + e.cost and !chk[e.to]) {
					que.push(e.to);
					chk[e.to] = 1;
				}
			}
		}
		while(!que.empty()) {
			int now = que.front();
			que.pop();
			for(auto &e : edges[now]) {
				if(!chk[e.to]) {
					chk[e.to] = 1;
					que.push(e.to);
				}
			}
		}
		for(int i = 0; i < n; i++)
			if(chk[i]) res[i] = -INF;
		return res;
	}

	// Ο(V^3)
	std::vector<std::vector<cost_type>> warshall_floyd() {
		int n = size();
		std::vector<std::vector<cost_type>> dist(n, std::vector<cost_type>(n, INF));
		for(int i = 0; i < n; i++) dist[i][i] = 0;
		for(int i = 0; i < n; i++)
			for(auto &e : edges[i]) dist[i][e.to] = std::min(dist[i][e.to], e.cost);
		for(int k = 0; k < n; k++)
			for(int i = 0; i < n; i++) {
				if(dist[i][k] == INF) continue;
				for(int j = 0; j < n; j++) {
					if(dist[k][j] == INF) continue;
					dist[i][j] = std::min(dist[i][j], dist[i][k] + dist[k][j]);
				}
			}
		return dist;
	}

	// Ο(V) (using DFS)
	// if a cycle exists, return {}
	std::vector<int> topological_sort() {
		std::vector<int> res;
		int n = size();
		std::vector<int> used(n, 0);
		bool not_DAG = false;
		auto dfs = [&](auto self, int k) -> void {
			if(not_DAG) return;
			if(used[k]) {
				if(used[k] == 1) not_DAG = true;
				return;
			}
			used[k] = 1;
			for(auto &e : edges[k]) self(self, e.to);
			used[k] = 2;
			res.push_back(k);
		};
		for(int i = 0; i < n; i++) dfs(dfs, i);
		if(not_DAG) return std::vector<int>{};
		std::reverse(res.begin(), res.end());
		return res;
	}

	bool is_dag() { return !topological_sort().empty(); }

	// Ο(V)
	// array of the distance to the most distant vertex
	// constraint: the graph is a tree
	std::vector<cost_type> height() {
		auto vec1 = bfs(0);
		int v1 = -1, v2 = -1;
		cost_type dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v1 = i;
		vec1 = bfs(v1);
		dia = -1;
		for(int i = 0; i < int(size()); i++)
			if(dia < vec1[i]) dia = vec1[i], v2 = i;
		auto vec2 = bfs(v2);
		for(int i = 0; i < int(size()); i++) {
			if(vec1[i] < vec2[i]) vec1[i] = vec2[i];
		}
		return vec1;
	}

	// O(V+E)
	// vector<(int)(0 or 1)>
	// if it is not bipartite, return {}
	std::vector<int> bipartite_grouping() {
		std::vector<int> colors(size(), -1);
		auto dfs = [&](auto self, int now, int col) -> bool {
			colors[now] = col;
			for(auto &e : edges[now]) {
				if(col == colors[e.to]) return false;
				if(colors[e.to] == -1 and !self(self, e.to, !col)) return false;
			}
			return true;
		};
		for(int i = 0; i < int(size()); i++)
			if(!colors[i] and !dfs(dfs, i, 0)) return std::vector<int>{};
		return colors;
	}

	bool is_bipartite() { return !bipartite_grouping().empty(); }

	// Ο(V+E)
	// (v1, v2, diameter)
	std::tuple<int, int, cost_type> diameter() {
		std::vector<cost_type> dist = bfs(0);
		auto it = std::max_element(dist.begin(), dist.end());
		const int v = it - dist.begin();
		dist = bfs(v);
		it = std::max_element(dist.begin(), dist.end());
		return std::make_tuple(v, int(it - dist.begin()), *it);
	}

	// Ο(ElgE)
	cost_type prim() {
		cost_type res = 0;
		std::priority_queue<edge, std::vector<edge>, std::greater<edge>> que;
		for(auto &e : edges[0]) que.push(e);
		std::vector<int> chk(size());
		chk[0] = 1;
		int cnt = 1;
		while(cnt < size()) {
			auto e = que.top();
			que.pop();
			if(chk[e.to]) continue;
			cnt++;
			res += e.cost;
			chk[e.to] = 1;
			for(auto &e2 : edges[e.to]) que.push(e2);
		}
		return res;
	}

	// Ο(ElgE)
	cost_type kruskal() {
		std::vector<std::tuple<int, int, cost_type>> edges;
		for(int i = 0; i < int(size()); i++)
			for(auto &e : edges[i]) edges.emplace_back(i, e.to, e.cost);
		std::sort(edges.begin(), edges.end(), [](const std::tuple<int, int, cost_type> &a, const std::tuple<int, int, cost_type> &b) {
			return std::get<2>(a) < std::get<2>(b);
		});
		std::vector<int> uf_data(size(), -1);
		auto root = [&uf_data](auto self, int x) -> int {
			if(uf_data[x] < 0) return x;
			return uf_data[x] = self(self, uf_data[x]);
		};
		auto unite = [&uf_data, &root](int u, int v) -> bool {
			u = root(root, u), v = root(root, v);
			if(u == v) return false;
			if(uf_data[u] > uf_data[v]) std::swap(u, v);
			uf_data[u] += uf_data[v];
			uf_data[v] = u;
			return true;
		};
		cost_type ret = 0;
		for(auto &e : edges)
			if(unite(std::get<0>(e), std::get<1>(e))) ret += std::get<2>(e);
		return ret;
	}

	// O(V)
	std::vector<int> centroid() {
		int n = size();
		std::vector<int> centroid, sz(n);
		auto dfs = [&](auto self, int now, int per) -> void {
			sz[now] = 1;
			bool is_centroid = true;
			for(auto &e : edges[now]) {
				if(e.to != per) {
					self(self, e.to, now);
					sz[now] += sz[e.to];
					if(sz[e.to] > n / 2) is_centroid = false;
				}
			}
			if(n - sz[now] > n / 2) is_centroid = false;
			if(is_centroid) centroid.push_back(now);
		};
		dfs(dfs, 0, -1);
		return centroid;
	}

	// O(V+E)
	// bridge: (s, t)  (s < t);
	std::pair<std::vector<std::pair<int, int>>, std::vector<int>> bridges_and_articulations() {	 // verified
		std::vector<int> order(size(), -1), low(size()), articulation;
		int order_next = 0;
		std::vector<std::pair<int, int>> bridge;
		auto dfs = [&](auto self, int now, int par = -1) -> void {
			low[now] = order[now] = order_next++;
			bool is_articulation = false;
			int cnt = 0;
			for(auto &ed : edges[now]) {
				int &nxt = ed.to;
				if(nxt == par) continue;
				if(order[nxt] == -1) {
					cnt++;
					self(self, nxt, now);
					if(order[now] < low[nxt]) bridge.push_back(std::minmax(now, nxt));
					if(order[now] <= low[nxt]) is_articulation = true;
					low[now] = std::min(low[now], low[nxt]);
				} else if(order[now] > order[nxt]) {
					low[now] = std::min(low[now], order[nxt]);
				}
			}
			if(par == -1 and cnt < 2) is_articulation = false;
			if(is_articulation) articulation.push_back(now);
			return;
		};
		for(int i = 0; i < (int)size(); i++)
			if(order[i] == -1) dfs(dfs, i);
		return std::make_pair(bridge, articulation);
	}

	// Ο(V+E)
	// directed graph from root to leaf
	graph root_to_leaf(int root = 0) {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(now, e.to, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// Ο(V+E)
	// directed graph from leaf to root
	graph leaf_to_root(int root = 0) {
		graph res(size());
		std::vector<int> chk(size(), 0);
		chk[root] = 1;
		auto dfs = [&](auto self, int now) -> void {
			for(auto &e : edges[now]) {
				if(chk[e.to] == 1) continue;
				chk[e.to] = 1;
				res.add_edge(e.to, now, e.cost, 1, 0);
				self(self, e.to);
			}
		};
		dfs(dfs, root);
		return res;
	}

	// cost_type Chu_Liu_Edmonds(int root = 0) {}
};
#pragma endregion
#line 2 "SPJ-Library/graph/tree_doubling.hpp"

using cost_type = int;
struct tree_doubling {
   private:
	std::vector<cost_type> dist;
	std::vector<int> depth;
	std::vector<std::vector<int>> parent;
	int max_jump = 1;

	void build() {
		for(int i = 0; i < max_jump - 1; i++) {
			for(int v = 0; v < (int)dist.size(); v++) {
				if(parent[i][v] == -1)
					parent[i + 1][v] = -1;
				else
					parent[i + 1][v] = parent[i][parent[i][v]];
			}
		}
	}

   public:
	tree_doubling() = default;
	tree_doubling(const graph<cost_type> &g, const int root = 0) : dist(g.size()), depth(g.size()) {
		int n = g.size();
		while((1 << max_jump) < n) max_jump++;
		parent.assign(max_jump, std::vector<int>(n, -1));
		auto dfs = [&](auto self, int now, int per, int d, cost_type cost) -> void {
			parent[0][now] = per;
			depth[now] = d;
			dist[now] = cost;
			for(auto &e : g[now])
				if(e.to != per) self(self, e.to, now, d + 1, cost + e.cost);
		};
		dfs(dfs, root, -1, 0, 0LL);
		build();
	}

	int lowest_common_ancestor(int u, int v) {
		if(depth[u] < depth[v]) std::swap(u, v);
		int k = int(parent.size());
		for(int i = 0; i < k; i++)
			if((depth[u] - depth[v]) >> i & 1) u = parent[i][u];
		if(u == v) return u;
		for(int i = k - 1; i >= 0; i--)
			if(parent[i][u] != parent[i][v]) u = parent[i][u], v = parent[i][v];
		return parent[0][u];
	}

	cost_type length_of_path(const int u, const int v) { return dist[u] + dist[v] - dist[lowest_common_ancestor(u, v)] * 2; }

	int level_ancestor(int v, int level) {
		assert(level >= 0);
		for(int jump = 0; jump < max_jump and level; jump++) {
			if(level & 1) v = parent[jump][v];
			level >>= 1;
		}
		return v;
	}
};
#line 616 "i.cpp"

V<mint> solve(int n) {
	graph<int> g(n, -1);
	const int rt = 0;
	V<mint> under(n);
	auto dfs = [&](auto self, int now, int par) -> void {
		mint &value = under[now] = 1;
		foa(e, g[now]) {
			if(e.to == par) continue;
			self(self, e.to, now);
			value += under[e.to] * 2;
		}
		return;
	};
	dfs(dfs, rt, -1);
	V<mint> up(n);
	auto anotherdfs = [&](auto self, int now, int par) -> void {
		const mint all_res = under[now] + up[now] * 2;
		foa(e, g[now]) {
			if(e.to == par) continue;
			up[e.to] = all_res - under[e.to] * 2;
			self(self, e.to, now);
		}
		return;
	};
	anotherdfs(anotherdfs, rt, -1);
	tree_doubling td(g, rt);
	V<mint> mp(1, 1);
	rep(n + 10) mp.push_back(mp.back() * 2);

	auto solve = [&](int x, int y) -> mint {
		int p = td.lowest_common_ancestor(x, y);
		int dist = td.length_of_path(x, y);
		if(x != p && y != p) return under[x] * under[y] * mp[dist];
		if(y == p) swap(x, y);
		// x == p
		int down_one = td.level_ancestor(y, dist - 1);
		mint ret = mp[dist];
		ret *= under[y];
		ret *= under[x] - under[down_one] * 2 + up[x] * 2;
		return ret;
	};

	int q;
	cin >> q;
	V<mint> ans;
	debug(under);
	rep(q) {
		int x, y;
		cin >> x >> y;
		ans.push_back(solve(--x, --y));
	}return ans;
}

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout << std::fixed << std::setprecision(15);
	srand((unsigned)time(NULL));

	int n;
	while(cin >> n) vout(solve(n), 1);
	return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 30088
final_rank: 3

code_snippet: #pragma GCC ta g("avx2")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define DEBUG
#ifdef DEBUG
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
    os << '(' << p.first << ',' << p.second << ')';
    return os;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    os << '{';
    for(int i = 0; i < (int)v.size(); i++) {
        if(i) { os << ','; }
        os << v[i];
    }
    os << '}';
    return os;
}
void debugg() { cerr << endl; }
template <class T, class... Args>
void debugg(const T &x, const Args &... args) {
    cerr << " " << x;
    debugg(args...);
}
#define debug(...)                                                             \
    cerr << __LINE__ << " [" << #__VA_ARGS__ << "]: ", debugg(__VA_ARGS__)
#define dump(x) cerr << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vl> vvl;
typedef vector<char> vc;
typedef vector<string> vs;
typedef vector<bool> vb;
typedef vector<double> vd;
typedef pair<ll,ll> P;
typedef pair<int,int> pii;
typedef vector<P> vpl;
typedef tuple<ll,ll,ll> tapu;
#define rep(i,n) for(int i=0; i<(n); i++)
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
template<typename T1,typename T2> inline bool chmin(T1 &a,T2 b){
	if(a>b){
		a = b; return true;
	}
	else return false;
}
template<typename T1,typename T2> inline bool chmax(T1 &a,T2 b){
	if(a<b){
		a = b; return true;
	}
	else return false;
}
template<typename T> inline void print(T &a){
    int sz = a.size();
    for(auto itr = a.begin(); itr != a.end(); itr++){
		cout << *itr;
        sz--;
        if(sz) cout << " ";
	}
    cout << "\n";
}
template<typename T1,typename T2> inline void print2(T1 a, T2 b){
	cout << a << " " << b << "\n";
}
template<typename T1,typename T2,typename T3> inline void print3(T1 a, T2 b, T3 c){
	cout << a << " " << b << " " << c << "\n";
}
void mark() {cout << "#" << "\n";}
ll pcount(ll x) {return __builtin_popcountll(x);}
//#include <atcoder/convolution>
//using namespace atcoder;
const int inf = (1<<30)-1;
const ll linf = 1LL<<61;
const int MAX = 510000;
int dy[8] = {0,1,0,-1,1,-1,-1,1};
int dx[8] = {-1,0,1,0,1,-1,1,-1};
const double pi = acos(-1);
const double eps = 1e-9;
//const int mod = 1e9 + 7;
const int mod = 998244353;

template <int mod> struct ModInt {
    int x;

    ModInt() : x(0) {}

    ModInt(int64_t y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

    ModInt &operator+=(const ModInt &p) {
        if((x += p.x) >= mod) x -= mod;
        return *this;
    }

    ModInt &operator-=(const ModInt &p) {
        if((x += mod - p.x) >= mod) x -= mod;
        return *this;
    }

    ModInt &operator*=(const ModInt &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }

    ModInt &operator/=(const ModInt &p) {
        *this *= p.inverse();
        return *this;
    }

    ModInt operator-() const { return ModInt(-x); }

    ModInt operator+(const ModInt &p) const { return ModInt(*this) += p; }

    ModInt operator-(const ModInt &p) const { return ModInt(*this) -= p; }

    ModInt operator*(const ModInt &p) const { return ModInt(*this) *= p; }

    ModInt operator/(const ModInt &p) const { return ModInt(*this) /= p; }

    bool operator==(const ModInt &p) const { return x == p.x; }

    bool operator!=(const ModInt &p) const { return x != p.x; }

    ModInt inverse() const {
        int a = x, b = mod, u = 1, v = 0, t;
        while(b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return ModInt(u);
    }

    ModInt pow(int64_t n) const {
        ModInt ret(1), mul(x);
        while(n > 0) {
            if(n & 1) ret *= mul;
            mul *= mul;
            n >>= 1;
        }
        return ret;
    }

    friend ostream &operator<<(ostream &os, const ModInt &p) {
        return os << p.x;
    }

    friend istream &operator>>(istream &is, ModInt &a) {
        int64_t t;
        is >> t;
        a = ModInt<mod>(t);
        return (is);
    }

    static int get_mod() { return mod; }
};

using mint = ModInt<mod>;

vvl G(101010);

struct LCA{
    ll logv = 0;
    ll root = 0;
    vvl parent;
    vl depth;

    LCA(ll n, ll r){
        root = r;
        depth.resize(n,0);
        while(1LL<<logv < n){
            logv++;
        }
        parent.resize(logv+1);
        rep(i,logv+1){
            parent[i].resize(n,-1);
        }
        dfs(root,-1,0);
        rep(i,logv){
            rep(j,n){
                if(parent[i][j] < 0) parent[i+1][j] = -1;
                else parent[i+1][j] = parent[i][parent[i][j]];
            }
        }
    }

    void dfs(ll u, ll p, ll d){
        parent[0][u] = p;
        depth[u] = d;
        for(auto v : G[u]){
            if(v != p) dfs(v,u,d+1);
        }
    }

    ll lca(ll u, ll v){
        if(depth[u] > depth[v]) swap(u,v);
        rep(i,logv){
            if((depth[v] - depth[u]) >> i & 1) v = parent[i][v];
        }
        if(u == v) return u;
        for(ll i=logv-1; i>=0; i--){
            if(parent[i][u] != parent[i][v]){
                u = parent[i][u];
                v = parent[i][v];
            }
        }
        return parent[0][u];
    }
};

int main(){
    int n; cin >> n;
    rep(i,n-1){
        int a,b; cin >> a >> b; a--; b--;
        G[a].push_back(b);
        G[b].push_back(a);
    }
    LCA lc(n,0);
    int q; cin >> q;
    vector<mint> ans(q);
    vl d(q);
    vl x(q),y(q);
    rep(i,q){
        cin >> x[i] >> y[i]; x[i]--; y[i]--;
    }
    vector<mint> val(n,1),res(n);
    vl ch(n,1);
    {
        auto dfs = [&](auto &&dfs, int u, int p) -> void {
            for(auto v : G[u]){
                if(v == p) continue;
                dfs(dfs,v,u);
                val[u] += val[v] * 2;
                ch[u] += ch[v];
            }
        };
        dfs(dfs,0,-1);
    }
    {
        res[0] = val[0];
        auto dfs = [&](auto &&dfs, int u, int p) -> void {
            if(u != 0){
                res[u] = (res[p] - val[u] * 2) * 2 + val[u];
            }
            for(auto v : G[u]){
                if(v == p) continue;
                dfs(dfs,v,u);
            }
        };
        dfs(dfs,0,-1);
    }
    //debug(res,val);
    vector<vpl> qu(n);
    rep(i,q){
        int p = lc.lca(x[i],y[i]);
        if(p == x[i]) qu[y[i]].emplace_back(x[i],i), d[i] = lc.depth[y[i]];
        else if(p == y[i]) qu[x[i]].emplace_back(y[i],i), d[i] = lc.depth[x[i]];
        else ans[i] = val[x[i]] * val[y[i]] * mint(2).pow(lc.depth[x[i]] + lc.depth[y[i]] - 2*lc.depth[p]);
    }
    vector<set<pii>> mt(n);
    auto dfs = [&](auto &&dfs, int u, int p) -> void {
        for(auto v : G[u]){
            if(v == p) continue;
            dfs(dfs,v,u);
            auto itr = mt[v].lower_bound(P(u,-inf));
            while(itr != mt[v].end() && (*itr).first == u){
                P pa = *itr;
                //debug(pa.first,u,d[pa.second],lc.depth[u]);
                ans[pa.second] *= mint(2).pow(d[pa.second] - lc.depth[u]);
                ans[pa.second] *= (res[u] - val[v] * 2);
                itr = mt[v].erase(itr);
            }
            if(mt[u].size() < mt[v].size()) swap(mt[u],mt[v]);
        }
        for(auto x : qu[u]){
            ans[x.second] = val[u];
            mt[u].emplace(x);
        }
    };
    dfs(dfs,0,-1);
    rep(i,q) cout << ans[i] << "\n";
}
accuracy: 0.09090909090909091
time_ms: 80
memory_kb: 33840
final_rank: 19

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

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif


#include <utility>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

constexpr long long safe_mod(long long x, long long m) {
    x %= m;
    if (x < 0) x += m;
    return x;
}

struct barrett {
    unsigned int _m;
    unsigned long long im;

    explicit barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}

    unsigned int umod() const { return _m; }

    unsigned int mul(unsigned int a, unsigned int b) const {

        unsigned long long z = a;
        z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};

constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;
        y = (y * y) % _m;
        n >>= 1;
    }
    return r;
}

constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    constexpr long long bases[3] = {2, 7, 61};
    for (long long a : bases) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);

constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};

    long long s = b, t = a;
    long long m0 = 0, m1 = 1;

    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b


        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}

constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        divs[cnt++] = x;
    }
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;
                break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);

unsigned long long floor_sum_unsigned(unsigned long long n,
                                      unsigned long long m,
                                      unsigned long long a,
                                      unsigned long long b) {
    unsigned long long ans = 0;
    while (true) {
        if (a >= m) {
            ans += n * (n - 1) / 2 * (a / m);
            a %= m;
        }
        if (b >= m) {
            ans += n * (b / m);
            b %= m;
        }

        unsigned long long y_max = a * n + b;
        if (y_max < m) break;
        n = (unsigned long long)(y_max / m);
        b = (unsigned long long)(y_max % m);
        std::swap(m, a);
    }
    return ans;
}

}  // namespace internal

}  // namespace atcoder


#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;

template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;

template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;

#else

template <class T> using is_integral = typename std::is_integral<T>;

template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;

#endif

template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;

template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;

template <class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder


namespace atcoder {

namespace internal {

struct modint_base {};
struct static_modint_base : modint_base {};

template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;

}  // namespace internal

template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;

  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};

template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;

  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt(998244353);

using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;

namespace internal {

template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;

template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;

template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};

template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;

}  // namespace internal

}  // namespace atcoder

using namespace atcoder;
#define rep(i,n) for(int i = 0; i < (int)(n); i++)
#define rrep(i,n) for(int i = (int)(n) - 1; i >= 0; i--)
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)
template<class T> bool chmax(T& a, const T& b) { if (a < b) { a = b; return true; } else return false; }
template<class T> bool chmin(T& a, const T& b) { if (b < a) { a = b; return true; } else return false; }
using ll = long long;
using P = pair<int,int>;
using VI = vector<int>;
using VVI = vector<VI>;
using VL = vector<ll>;
using VVL = vector<VL>;
using mint = modint998244353;

template<class S, class E>
struct Rerooting {
    const vector<vector<E>>& g;
    const int n;
    const int root;
    vector<vector<S>> dp, sl, sr;

    Rerooting(const vector<vector<E>>& g, int root=0): g(g), n(g.size()), root(root), dp(n), sl(n), sr(n) {
        dfs1(root);
        dfs2(root);
    }

    S dfs1(int u, int p=-1) {
        const int sz = g[u].size();
        dp[u].resize(sz);
        sl[u].resize(sz + 1);
        sr[u].resize(sz + 1);

        S res;
        for(int i = 0; i < sz; i++) {
            const E& e = g[u][i];
            int v = dest(e);
            if (v == p) continue;
            dp[u][i] = dfs1(v, u).apply(e);
            res = res.merge(dp[u][i]);
        }
        return res;
    }

    void dfs2(int u, int p=-1) {
        const int sz = g[u].size();

        {
            S s;
            for(int i = 0; i < sz; i++) {
                s = s.merge(dp[u][i]);
                sl[u][i + 1] = s;
            }
        }
        {
            S s;
            for(int i = sz - 1; i >= 0; i--) {
                s = dp[u][i].merge(s);
                sr[u][i] = s;
            }
        }

        for(int i = 0; i < sz; i++) {
            int v = dest(g[u][i]);
            if (v == p) continue;
            const int sz_v = g[v].size();
            for(int j = 0; j < sz_v; j++) {
                const E& e = g[v][j];
                int w = dest(e);
                if (w != u) continue;
                dp[v][j] = sl[u][i].merge(sr[u][i + 1]).apply(e);
                break;
            }
            dfs2(v, u);
        }
    }

    S get_acc(int v) { return sr[v][0]; }
    S get_res(int v, E e) { return sr[v][0].apply(e); }

    private:
    int dest(const E& e) {
        if constexpr (is_same<E, int>::value) return e;
        else return e.to;
    };
};


struct S {
    mint v;
    S apply(int e) const { return {1 + 2 * v}; }
    S merge(const S& rhs) const { return S{v + rhs.v}; }
};

struct HLD {
    std::vector<std::vector<int>> &to;
    int root, n;
    std::vector<int> sz, parent, depth, idx, ridx, head, inv;
    HLD(std::vector<std::vector<int>>& to, int root=0):
        to(to), root(root), n(to.size()),
        sz(n), parent(n), depth(n), idx(n), ridx(n), head(n), inv(n)
    {
        init_tree_data(root);
        int x = 0;
        assign_idx(root, root, x);
    }
    void init_tree_data(int u, int p=-1, int d=0) {
        parent[u] = p;
        depth[u] = d;
        int s = 1;
        for(int v: to[u]) {
            if (v == p) continue;
            init_tree_data(v, u, d+1);
            s += sz[v];
        }
        sz[u] = s;
    }
    void assign_idx(int u, int h, int &nxt, int p=-1) {
        head[u] = h;
        inv[nxt] = u;
        idx[u] = nxt++;
        if (sz[u] == 1) {
            ridx[u] = nxt;
            return;
        }
        int mxsize = 0;
        int mi;
        for(int v: to[u]) {
            if (v == p) continue;
            if (sz[v] > mxsize) {
                mxsize = sz[v];
                mi = v;
            }
        }
        assign_idx(mi, h, nxt, u);
        for(int v: to[u]) {
            if (v == p || v == mi) continue;
            assign_idx(v, v, nxt, u);
        }
        ridx[u] = nxt;
    }

    int lca(int u, int v) {
        while(head[u] != head[v]) {
            if (depth[head[u]] > depth[head[v]]) u = parent[head[u]];
            else v = parent[head[v]];
        }
        return (depth[u] < depth[v] ? u : v);
    }
    // returns (paths upto lca from x (excluding lca), those from y, lca)
    std::tuple<std::vector<std::pair<int, int>>, std::vector<std::pair<int, int>>, int> paths(int x, int y) {
        std::tuple<std::vector<std::pair<int, int>>, std::vector<std::pair<int, int>>, int> ret;
        std::vector<std::pair<int, int>>& x_paths = get<0>(ret);
        std::vector<std::pair<int, int>>& y_paths = get<1>(ret);
        int& lca = get<2>(ret);
        while(head[x] != head[y]) {
            int xhead = head[x], yhead = head[y];
            if (depth[xhead] > depth[yhead]) {
                x_paths.emplace_back(x, xhead);
                x = parent[xhead];
            } else {
                y_paths.emplace_back(y, yhead);
                y = parent[yhead];
            }
        }
        if (depth[x] > depth[y]) {
            int ychild = inv[idx[y] + 1];
            x_paths.emplace_back(x, ychild);
            x = y;
        } else if (depth[x] < depth[y]) {
            int xchild = inv[idx[x] + 1];
            y_paths.emplace_back(y, xchild);
            y = x;
        }
        lca = x;
        return ret;
    }
    
    int dist(int u, int v) {
        int w = lca(u, v);
        return depth[u] + depth[v] - 2 * depth[w];
    }
};

} int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  int n;
  cin >> n;
  VVI to(n);
  vector<map<int, int>> idx(n);
  rep(_, n - 1) {
    int a, b;
    cin >> a >> b;
    a--, b--;
    idx[a][b] = to[a].size();
    to[a].emplace_back(b);
    idx[b][a] = to[b].size();
    to[b].emplace_back(a);
  }
  Rerooting<S, int> rt(to);
  HLD hld(to);
  int q;
  cin >> q;
  while(q--) {
    int x, y;
    cin >> x >> y;
    x--, y--;
    auto [pathx, pathy, lca] = hld.paths(x, y);
    int px=-1, py=-1;
    rep(_, 2) {
      if (!pathx.empty()) {
        px = hld.parent[x];
      } else {
        px = pathy.back().second;
      }
      swap(x, y);
      swap(px, py);
      swap(pathx, pathy);
    }
    int dist = hld.dist(x, y);
    int ix = idx[x][px], iy = idx[y][py];
    mint a = rt.sl[x][ix].merge(rt.sr[x][ix+1]).apply(-1).v;
    mint b = rt.sl[y][iy].merge(rt.sr[y][iy+1]).apply(-1).v;
    cout << (a * b * mint(2).pow(dist)).val() << '\n';
  }
}
accuracy: 1
time_ms: 120
memory_kb: 49544
final_rank: 10

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



#include <utility>

namespace atcoder {

namespace internal {

// @param m `1 <= m`
// @return x mod m
constexpr long long safe_mod(long long x, long long m) {
    x %= m;
    if (x < 0) x += m;
    return x;
}

// Fast modular multiplication by barrett reduction
// Reference: https://en.wikipedia.org/wiki/Barrett_reduction
// NOTE: reconsider after Ice Lake
struct barrett {
    unsigned int _m;
    unsigned long long im;

    // @param m `1 <= m < 2^31`
    barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}

    // @return m
    unsigned int umod() const { return _m; }

    // @param a `0 <= a < m`
    // @param b `0 <= b < m`
    // @return `a * b % m`
    unsigned int mul(unsigned int a, unsigned int b) const {
        // [1] m = 1
        // a = b = im = 0, so okay

        // [2] m >= 2
        // im = ceil(2^64 / m)
        // -> im * m = 2^64 + r (0 <= r < m)
        // let z = a*b = c*m + d (0 <= c, d < m)
        // a*b * im = (c*m + d) * im = c*(im*m) + d*im = c*2^64 + c*r + d*im
        // c*r + d*im < m * m + m * im < m * m + 2^64 + m <= 2^64 + m * (m + 1) < 2^64 * 2
        // ((ab * im) >> 64) == c or c + 1
        unsigned long long z = a;
        z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};

// @param n `0 <= n`
// @param m `1 <= m`
// @return `(x ** n) % m`
constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;
        y = (y * y) % _m;
        n >>= 1;
    }
    return r;
}

// Reference:
// M. Forisek and J. Jancina,
// Fast Primality Testing for Integers That Fit into a Machine Word
// @param n `0 <= n`
constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    constexpr long long bases[3] = {2, 7, 61};
    for (long long a : bases) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);

// @param b `1 <= b`
// @return pair(g, x) s.t. g = gcd(a, b), xa = g (mod b), 0 <= x < b/g
constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};

    // Contracts:
    // [1] s - m0 * a = 0 (mod b)
    // [2] t - m1 * a = 0 (mod b)
    // [3] s * |m1| + t * |m0| <= b
    long long s = b, t = a;
    long long m0 = 0, m1 = 1;

    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b

        // [3]:
        // (s - t * u) * |m1| + t * |m0 - m1 * u|
        // <= s * |m1| - t * u * |m1| + t * (|m0| + |m1| * u)
        // = s * |m1| + t * |m0| <= b

        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    // by [3]: |m0| <= b/g
    // by g != b: |m0| < b/g
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}

// Compile time primitive root
// @param m must be prime
// @return primitive root (and minimum in now)
constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        divs[cnt++] = x;
    }
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;
                break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);

}  // namespace internal

}  // namespace atcoder


#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;

template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;

template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;

#else

template <class T> using is_integral = typename std::is_integral<T>;

template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;

#endif

template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;

template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;

template <class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

struct modint_base {};
struct static_modint_base : modint_base {};

template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;

}  // namespace internal

template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;

  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }
    static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};

template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;

  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }
    dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;

using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;

namespace internal {

template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;

template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;

template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};

template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;

}  // namespace internal

}  // namespace atcoder



#include <vector>

template <class T = int>
struct Edge {
    int from, to;
    T cost;
    int idx;

    Edge() = default;

    Edge(int from, int to, T cost = 1, int idx = 0)
        : from(from), to(to), cost(cost), idx(idx) {}
};

template <class T = int>
struct Graph {
    std::vector<std::vector<Edge<T>>> es;
    int edge_num;

    Graph(int n) : edge_num(0) { es.resize(n); }

    int size() { return es.size(); }

    void add_edge(int from, int to, T cost, int idx) {
        es[from].emplace_back(from, to, cost, idx);
        ++edge_num;
    }

    std::vector<Edge<T>> edges() {
        std::vector<Edge<T>> res(edge_num);
        for (int v = 0; v < (int)es.size(); ++v) {
            for (auto& e : es[v]) {
                res[e.idx] = e;
            }
        }
        return res;
    }

    std::vector<Edge<T>>& operator[](int i) { return es[i]; }
};


#include <algorithm>
#include <cassert>
#include <vector>

template <class T>
bool chmax(T &a, const T &b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}

template <class T>
bool chmin(T &a, const T &b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}

template <class T>
T div_floor(T a, T b) {
    if (b < 0) a *= -1, b *= -1;
    return a >= 0 ? a / b : (a + 1) / b - 1;
}

template <class T>
T div_ceil(T a, T b) {
    if (b < 0) a *= -1, b *= -1;
    return a > 0 ? (a - 1) / b + 1 : a / b;
}

template <typename T>
struct CoordComp {
    std::vector<T> v;
    bool sorted;

    CoordComp() : sorted(false) {}

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

    void add(T x) { v.push_back(x); }

    void build() {
        std::sort(v.begin(), v.end());

        v.erase(std::unique(v.begin(), v.end()), v.end());
        sorted = true;
    }

    int get_idx(T x) {
        assert(sorted);
        return lower_bound(v.begin(), v.end(), x) - v.begin();
    }

    T &operator[](int i) { return v[i]; }
};


struct HeavyLightDecomposition {
   public:
    std::vector<int> idx, par, head, out;

    HeavyLightDecomposition(Graph<> gr, int root = 0) {
        idx.resize(gr.size());
        par.resize(gr.size());
        head.resize(gr.size());
        out.resize(gr.size());

        sz_dfs(gr, root, -1);
        int cur = 0;
        idx[root] = cur++;
        head[root] = root;
        hld_dfs(gr, root, -1, root, cur);
    }

    int lca(int u, int v) {
        while (true) {
            if (idx[u] > idx[v]) std::swap(u, v);
            if (head[u] == head[v]) return u;
            v = par[head[v]];
        }
    }

    std::pair<std::vector<std::pair<int, int>>,
              std::vector<std::pair<int, int>>>
    get_path(int u, int v) {
        std::vector<std::pair<int, int>> up, down;
        while (true) {
            if (head[u] == head[v]) {
                if (idx[u] < idx[v])
                    down.emplace_back(idx[u], idx[v] + 1);
                else
                    up.emplace_back(idx[v], idx[u] + 1);
                std::reverse(up.begin(), up.end());
                std::reverse(down.begin(), down.end());
                return {up, down};
            } else {
                if (idx[u] < idx[v]) {
                    down.emplace_back(idx[head[v]], idx[v] + 1);
                    v = par[head[v]];
                } else {
                    up.emplace_back(idx[head[u]], idx[u] + 1);
                    u = par[head[u]];
                }
            }
        }
    }

   private:
    int sz_dfs(Graph<> &gr, int v, int pv) {
        int ret = 1, max_sz = 0;
        for (int i = 0; i < (int)gr[v].size(); ++i) {
            if (gr[v][i].to == pv) continue;
            int tmp = sz_dfs(gr, gr[v][i].to, v);
            ret += tmp;
            if (chmax(max_sz, tmp)) std::swap(gr[v][0], gr[v][i]);
        }
        return ret;
    }

    void hld_dfs(Graph<> &gr, int v, int pv, int h, int &cur) {
        for (auto &e : gr[v]) {
            if (e.to == pv) continue;
            idx[e.to] = cur++;  // バグっている気がする
            par[e.to] = v;
            head[e.to] = (gr[v][0].to == e.to ? h : e.to);
            hld_dfs(gr, e.to, v, head[e.to], cur);
        }
        out[v] = cur;
    }
};


#define For(i, a, b) for (int i = (int)(a); (i) < (int)(b); ++(i))
#define rFor(i, a, b) for (int i = (int)(a)-1; (i) >= (int)(b); --(i))
#define rep(i, n) For(i, 0, n)
#define rrep(i, n) rFor(i, n, 0)
#define fi first
#define se second

using namespace std;

using lint = long long;
using pii = pair<int, int>;
using pll = pair<lint, lint>;

using mint = atcoder::modint998244353;

constexpr int MAX = 100010;
int depth[MAX], idx_inv[MAX];
mint dp_subtree[MAX], dp_par[MAX];

void dfs_subtree(int v, int pv, int dep, Graph<int> &gr) {
    depth[v] = dep;
    dp_subtree[v] = 1;
    for (auto &e : gr[v]) {
        if (e.to == pv) continue;
        dfs_subtree(e.to, v, dep + 1, gr);
        dp_subtree[v] += dp_subtree[e.to] * 2;
    }
}

void dfs_par(int v, int pv, mint par, Graph<int> &gr) {
    dp_par[v] = par + dp_subtree[v];
    for (auto &e : gr[v]) {
        if (e.to == pv) continue;
        dfs_par(e.to, v, (dp_par[v] - dp_subtree[e.to] * 2) * 2, gr);
    }
}

int level_ancestor(int v, int l, HeavyLightDecomposition &hld) {
    while (l > 0) {
        int tmp = depth[v] - depth[hld.head[v]];
        if (tmp >= l) {
            return idx_inv[hld.idx[v] - l];
        } else {
            l -= tmp + 1;
            v = hld.par[hld.head[v]];
        }
    }
    return v;
}

int main() {
    int n;
    scanf("%d", &n);
    Graph<int> gr(n);
    rep(i, n - 1) {
        int a, b;
        scanf("%d%d", &a, &b);
        --a;
        --b;
        gr.add_edge(a, b, 1, i);
        gr.add_edge(b, a, 1, i);
    }
    HeavyLightDecomposition hld(gr);
    rep(i, n) idx_inv[hld.idx[i]] = i;
    dfs_subtree(0, -1, 0, gr);
    dfs_par(0, -1, 0, gr);

    int q;
    scanf("%d", &q);
    rep(qq, q) {
        int x, y;
        scanf("%d%d", &x, &y);
        --x;
        --y;
        int lca = hld.lca(x, y);
        if (x != lca && y != lca) {
            int len = depth[x] + depth[y] - 2 * depth[lca];
            mint ans = dp_subtree[x] * mint(2).pow(len) * dp_subtree[y];
            printf("%u\n", ans.val());
        } else {
            if (y == lca) swap(x, y);
            int len = depth[y] - depth[x];
            int c = level_ancestor(y, len - 1, hld);
            mint ans = dp_subtree[y] * mint(2).pow(len) *
                       (dp_par[x] - dp_subtree[c] * 2);
            printf("%u\n", ans.val());
        }
    }
}
accuracy: 1
time_ms: 50
memory_kb: 28772
final_rank: 1