QOJ.ac

QOJ

ID题目提交者结果用时内存语言文件大小提交时间测评时间
#694417#9530. A Game On TreeRUOHUIWA 0ms3812kbC++203.8kb2024-10-31 17:54:282024-10-31 17:54:44

Judging History

This is the latest submission verdict.

  • [2024-10-31 17:54:44]
  • Judged
  • Verdict: WA
  • Time: 0ms
  • Memory: 3812kb
  • [2024-10-31 17:54:28]
  • Submitted

answer

#include "bits/stdc++.h"
#define int long long
#define ull unsigned long long
#define double long double
#define PII pair<int,int>
#define TIII tuple<int,int,int>
#define LL __int128
#define eps 1e-6
using namespace std;
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
constexpr int N = 2e5 + 10, M = 2e6 + 10, mod = 998244353, inf = 1e18;
int n, m;

int ksm(int a, int k)
{
    int ans = 1;
    while (k)
    {
        if (k & 1) ans = ans * a % mod;
        a = a * a % mod;
        k >>= 1;
    }
    return ans;
}

int inv(int x)
{
    return ksm(x, mod - 2);
}

void solve()
{
    cin >> n;
    vector adj(n + 1, vector<int>());
    for (int i = 1; i <= n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        adj[u].emplace_back(v), adj[v].emplace_back(u);
    }
    /*    
     * 所有路径的贡献都可以表示为 a ^ 2 * b ^ 2 == (a * b) ^ 2 表示两个人路径端点的选择
     * 一条路径上的公共边(e[i], e[i + 1]....e[j])的贡献为 SUM(e[i]) + SUM(e[i] * e[j])
     * 即贡献可以分为两部分 1、该边做为公共边的贡献  2、该边做为两条路径上的公共边对的贡献
     * 对于第一种贡献: 直接遍历所有边(u, v)的贡献[(n - size[v]) * size[v])] ^ 2
     * 对于第二种贡献 考虑通过点 u 的所有路径:
     * (1)、 两条边在 u 的同一子树:
     * 必然固定一边(u, v) 一端点(n - size[v]) 另一端点在 v 树中选取
     * 显然对于 v 的直系儿子的贡献为son[v] * 1 
     * 对于 v 的儿子的儿子 显然路径上公共边 + 1 贡献为2 * son[son[v]] 依次类推
     * 会发现贡献的次数与深度有关 深度越大 贡献次数越多 而size[u]表示的是 u 树的大小
     * 如果 v 向下还能延展两层(v1 v2 表示 v 的儿子 孙子) 则贡献为 [(size[v1] + size[v2]) * (n - size[v])] ^ 2
     * 在 v2 选择另一个端点的公共边有两条(v, v1) (v1, v2) 而恰好被计算了两次
     * 进而自然地令sum[u]为 u 树的所有子树大小的平方和
     * 所以两条边在 u 的同一子树的贡献即为(n - size[v]) ^ 2 * (sum[u] - size[v] *size[v])
     * (2)、两条边在 u 的不同子树:
     * 显然通过上面的推导贡献为SUM(sum[v1] * sum[v2]) (v1 v2 表示两个不同子树)
     * 在 u 遍历当前子树 v 时(之前已经遍历完一些子树) 贡献即为 sum[u] * sum[v] 
     *                                                 sum[u] += sum[v]  后更新sum[u]
   */
    vector<int> size(n + 1), sum(n + 1);
    int ans = 0; //总期望
    function<void(int, int)> dfs = [&](int u, int fa)
    {
        size[u] = 1;
        for (auto &v: adj[u])
        {
            if (v == fa) continue;
            dfs(v, u);
            //1、通过该边的贡献(在 v 树和 v 树之外选择两个端点做为路径端点)
            ans += (n - size[v]) * size[v] % mod * (n - size[v]) % mod * size[v] % mod;
            ans %= mod;
            ans += 2 * (n - size[v]) % mod * (n - size[v]) % mod * (sum[u] - size[v] * size[v] % mod) % mod;
            ans = (ans + mod) % mod;
            ans += 2 * sum[u] % mod * sum[v] % mod;
            ans %= mod;
            size[u] += size[v];
            sum[u] = (sum[u] + sum[v]) % mod;
        }
        sum[u] += size[u] * size[u] % mod;
        sum[u] %= mod;
    };
    dfs(1, 0);
    int base = (n - 1) * (n - 1) % mod * n % mod * n % mod;
    cout << ans * 4 % mod * inv(base) % mod << endl;
}

signed main()
{
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    cout << fixed << setprecision(2);

#ifndef ONLINE_JUDGE
    freopen("in.txt", "rt", stdin);
    freopen("out.txt", "wt", stdout);
#endif

    int Cases = 1;
    cin >> Cases;
    while (Cases--)
    {
        solve();
    }
    return 0;
}

詳細信息

Test #1:

score: 0
Wrong Answer
time: 0ms
memory: 3812kb

input:

2
3
1 2
2 3
5
1 2
1 5
3 2
4 2

output:

443664156
918384808

result:

wrong answer 1st lines differ - expected: '443664158', found: '443664156'