QOJ.ac
QOJ
ID | 题目 | 提交者 | 结果 | 用时 | 内存 | 语言 | 文件大小 | 提交时间 | 测评时间 |
---|---|---|---|---|---|---|---|---|---|
#694417 | #9530. A Game On Tree | RUOHUI | WA | 0ms | 3812kb | C++20 | 3.8kb | 2024-10-31 17:54:28 | 2024-10-31 17:54:44 |
Judging History
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'