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

void solve(std::istream &in, std::ostream &out);

int main() {
#ifdef LOCAL
  freopen("../IO/f.in", "r", stdin);
//  freopen("../IO/f.out", "w", stdout);
#else
  std::ios_base::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cout.tie(nullptr);
#endif
  solve(std::cin, std::cout);
  return 0;
}

int md = 998244353;
constexpr int N = (int)2e5 + 499;
vector<int> g[N];
int par[N], dp[N][3][3];
// dp[V][A][B] - number of ways to choose labels in subtree of V such that |label(v) - label(u)| <= 1 for adjacent vertices
// and there are exactly A candidates for label D/2 and B candidates for label -D/2
// since we are interested in labeling with exactly one vertex of both types, it's enough to count number of labelings with more than one candidate instead of an exact number.
// since root has value 0, the problem is reduced to assigning edge weights.
void dfs1(int v, int p, int d, int &cand, int &mx) {
  par[v] = p;
  if (d > mx)
    mx = d, cand = v;
  for (int u : g[v])
    if (u != p)
      dfs1(u, v, d + 1, cand, mx);
}

void treedp(int v, int p, int left) {
  if (left == 0) {
    // depending on the parent-edge value this vertex-candidate may result in states (1, 0), (0, 0), and(0, 1)
    // so it's ok to say that this vertex is a candidate for both -D/2 and D/2
    dp[v][1][1] = 1;
  } else {
    dp[v][0][0] = 1;
  }
  for (int u : g[v]) {
    if (u == p) continue;
    treedp(u, v, left - 1);
    int ndp[3][3] = {};
    for (int a1 : {0, 1, 2}) {
      for (int b1 : {0, 1, 2}) {
        for (int a2 : {0, 1, 2}) {
          for (int b2 : {0, 1, 2}) {
            // +1
            ndp[min(a1 + a2, 2)][b1] = (ndp[min(a1 + a2, 2)][b1] + (int64_t) dp[v][a1][b1] * dp[u][a2][b2] % md) % md;
            // 0
            ndp[a1][b1] = (ndp[a1][b1] + (int64_t) dp[v][a1][b1] * dp[u][a2][b2] % md) % md;
            // -1
            ndp[a1][min(b1 + b2, 2)] = (ndp[a1][min(b1 + b2, 2)] + (int64_t) dp[v][a1][b1] * dp[u][a2][b2] % md) % md;
          }
        }
      }
    }
    for (int a1 : {0, 1, 2}) {
      for (int a2 : {0, 1, 2}) {
        dp[v][a1][a2] = ndp[a1][a2];
      }
    }
  }
}

void solve(std::istream &in, std::ostream &out) {
  int n;
  in >> n;
  for (int i = 0, u, v; i < n - 1; ++i) {
    in >> u >> v;
    --u;
    --v;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  int u, v, mx = 0;
  dfs1(0, -1, 0, u, mx);
  mx = 0;
  dfs1(u, -1, 0, v, mx);
  for (int i = 0; i < mx / 2; ++i) {
    v = par[v];
    u = par[v];
  }
  if (mx % 2 == 1) {
    treedp(v, u, mx / 2);
    treedp(u, v, mx / 2);
    // in fact, any labeling that has at least one -(D - 1)/2 path is ok too
    // because none of them can form a diameter with (D - 1)/2 path as the resulting path will have length D-1 without an edge u-v
    // division by 2 in not needed since it's only (D-1)/2 path that will be paired with the other half
    // if we multiply signs by -1 the other part will be paired, and thus another diameter will be formed
    int x = ((int64_t) dp[v][1][0] + dp[v][1][1] + dp[v][1][2]) % md;
    int y = ((int64_t) dp[u][1][0] + dp[u][1][1] + dp[u][1][2]) % md;
    out << (int64_t) x * y % md << '\n';
  } else {
    treedp(v, -1, mx / 2);
    // for a fixed diameter we can multiply labels by -1 and still have a valid labeling with the same diameter
    // thus dividing by 2 is needed
    out << (int64_t) dp[v][1][1] * (md + 1) / 2 % md << '\n';
  }
}


Measured memory_bytes: 29312