首页 > 其他分享 >2024CCPC哈尔滨 L 题解

2024CCPC哈尔滨 L 题解

时间:2024-11-02 21:31:25浏览次数:4  
标签:int 题解 sum 路径 哈尔滨 i64 siz2 2024CCPC siz

思路

首先可以发现这个期望其实是假的,我们只需要把所有方案的答案加起来,最后除以 \((\frac{n(n-1)}{2})^2\) 即可,现在考虑如何统计所有方案的答案。

我们先考虑一条路径的方案数:假设存在一条从 \(x\) 到 \(y\) 的公共路径,其中 \(x\) 是 \(y\) 的祖先,那么小红和小蓝分别选择的路径,其中一边的端点肯定在 \(y\) 的子树内,总共的方案数为 \(siz[y]^2\)。但是这并不正确,我们不妨记 \(y\) 的儿子为 \(y_1, y_2,...,y_m\),那么如果选择的端点都落在 \(y_1\) 内,那么实际的公共路径就变为了 \(x \rightarrow y_1\),所以实际的方案数是 \(siz[y]^2- \sum_{y_i \in son_y}siz[y_i]^2\),下面我们记 \(siz2[y]\) 为 \(\sum_{y_i \in son_y}siz[y_i]^2\)。

知道这个之后,我们可以考虑树形dp,由于 \((a+1)^2=a^2+2a+1\),所以我们可以考虑维护所有路径的 \(i\) 次项和,即方案数乘路径长度的 \(i\) 次方。

记 \(f[x][0/1/2]\) 为 \(x\) 子树内所有到 \(x\) 的路径的 \(0/1/2\) 次项之和,现在考虑转移:记 \(y\) 为 \(x\) 的一个儿子,现在我们要把 \(y\) 向 \(x\) 合并,首先因为会加入 \(x-y\) 这条边,所以所有边的长度都会加一,即:

\[\begin{align} f[y][0] = &f[y][0] \notag \\ f[y][1] = &f[y][1] + f[y][0] \notag \\ f[y][2] = &f[y][2] + 2f[y][1] + f[y][0] \notag \end{align} \]

同时 \(x-y\) 这条边也要算进我们的方案数,即:

\[f[y][i] = f[y][i] + siz[y]^2 - siz2[y] \ (0 \leq i \leq 2) \]

这样处理完之后直接加到 \(f[x]\) 里即可。

考虑完转移,现在我们考虑如何计算答案,同样是 \(y\) 向 \(x\) 转移的过程,我f们可以从 \(x\) 和 \(y\) 里面分别选出一条路径拼成一条完整的路径,记两条路径分别为 \(h_1,h_2\),同时对应的方案数为 \(g(h_1), g(h_2)\),有:

\[\begin{align} \Delta ans = & \sum_{h_1} \sum_{h_2} g(h_1)g(h_2)(|h_1| + |h_2|)^2 \notag \\ = & \sum_{h_1} \sum_{h_2} g(h_1)g(h_2)(|h_1|^2 + 2|h_1||h_2| + |h_2|^2) \notag \\ = & \sum_{h_1}g(h_1) \sum_{h_2}g(h_2)|h_2|^2 + 2\sum_{h_1}g(h_1)|h_1| \sum{h_2}g(h_2)|h_2| + \sum_{h_2}g(h_2) \sum_{h_1}g(h_1)|h_1|^2 \notag \\ = & f[x][0] \times f[y][2] + 2 \times f[x][1] \times f[y][1] + f[x][2] \times f[y][0] \notag \end{align} \]

除此之外,\(y\) 中的路径也可以单独成为一条可行的路径,这种情况下 \(x\) 侧端点的方案数为

\[(n - siz[y]) ^ 2 - (siz2[x] - siz[y]^2) - (n - siz[x]) ^ 2 \]

即两个端点不能都在同一个 \(x\) 的儿子的子树内,也不能都在 \(x\) 的父亲之外,这里可以自己画画图理解一下。

到这里就把这道题解决了,更多细节见代码

代码

#include <bits/stdc++.h>

using i64 = long long;

constexpr int P = 998244353;

i64 power(i64 a, i64 b)
{
	i64 res = 1;
	for( ; b; b >>= 1, a = a * a % P)
		if(b & 1) res = res * a % P;

	return res;
}

void solve()
{
	int n; std::cin >> n;

	std::vector<std::vector<int>> adj(n);
	for(int i = 1; i < n; i++)
	{
		int u, v; std::cin >> u >> v;
		u--, v--;

		adj[u].emplace_back(v);
		adj[v].emplace_back(u);
	}

	std::vector<i64> siz(n), siz2(n);
	auto init = [&](auto init, int x, int fa) -> void
	{	
		siz[x] = 1;
		for(auto y : adj[x])
		{
			if(y == fa) continue;

			init(init, y, x);
			siz[x] += siz[y];
			siz2[x] += siz[y] * siz[y]; 
		}
	};	
	init(init, 0, -1);

	std::vector f(n, std::vector<i64>(3));
	i64 ans = 0;
	auto dfs = [&](auto dfs, int x, int fa) -> void
	{
		for(auto y : adj[x])
		{
			if(y == fa) continue;

			dfs(dfs, y, x);

			f[y][2] = (f[y][2] + 2 * f[y][1] + f[y][0]) % P;
			f[y][1] = (f[y][1] + f[y][0]) % P;
			for(int i = 0; i < 3; i++) f[y][i] = (f[y][i] + siz[y] * siz[y] - siz2[y]) % P;

			i64 res = (f[y][0] * f[x][2] + f[y][2] * f[x][0] + 2 * f[y][1] * f[x][1]) % P;
			i64 res2 = f[y][2] * ((n - siz[y]) * (n - siz[y]) - (siz2[x] - siz[y] * siz[y]) - (n - siz[x]) * (n - siz[x]));

			ans = (ans + res + res2) % P;
			for(int i = 0; i < 3; i++) f[x][i] = (f[x][i] + f[y][i]) % P;
		}
	};
	dfs(dfs, 0, -1);

	i64 inv = power(1LL * n * (n - 1) / 2 % P, P - 2);
	ans = ans * inv % P * inv % P;

	std::cout << ans << "\n";
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int t; std::cin >> t;
	while(t--) solve();

	return 0;
}

标签:int,题解,sum,路径,哈尔滨,i64,siz2,2024CCPC,siz
From: https://www.cnblogs.com/Repeater11/p/18522488

相关文章

  • 第八届御网杯线下赛Pwn方向题解
    由于最近比赛有点多,而且赶上招新,导致原本应该及时总结的比赛搁置了,总结来说还是得多练,因为时间很短像这种线下赛,一般只有几个小时,所以思路一定要清晰,我还是经验太少了,导致比赛力不从心,先鸽了~Skillchecksec检查保护(没有开PIE和Canary)ida逆向分析一下不同的选项对应不......
  • Codeforces Round 983 (Div. 2) 题解
    CodeforcesRound983(Div.2)题解CodeforcesRound983(Div.2)Problem-A-Codeforces解题思路考虑贪心,每个灯连两个开关,即两个开的灯可以关闭一盏灯,则灯数最多则尽可能让两个开关都开的灯尽量少,灯数最少则反之#include<bits/stdc++.h>#defineendl'\n'usingnam......
  • 线段树也能是 Trie 树 题解
    题意简述给定一个长为\(n=2^k\)的序列\(\{a_0,\ldots,a_{n-1}\}\),你需要使用数据结构维护它,支持\(m\)次以下操作:单点加:\(a_x\getsa_x+y\);区间查:\(\sum\limits_{i=l}^ra_i\);全局下标与:\(a'_{i\operatorname{and}x}\getsa_{i}\),即把\(a_i\)累加到......
  • 动态规划题解报告
    [APIO2016]划艇注意到\(n\le500\)考虑\(O(n^3)\)的做法。值域小的做法比较显然,值域比较大,考虑离散化(将\(b_i+1\)然后限制变为\([a_i,b_i+1)\))。设\(f_{i,j}\)表示考虑前\(i\)个,\(i\)选择\(j\)的方案数。发现由于离散化了很难转移\(f_{k,j}\(k<i)\)的情况......
  • 2024 XCPC 哈尔滨 & Chengdu 游记
    CCPCDay-1​第一次坐飞机,起飞后世界瞬间变得好小,白云在我面前流过,河上的船一动不动.随后出现的积云构成了冰川,剩余稀薄的云雾掩盖下面的城市,成为一片蓝色的海.视线的尽头,我看到了被深蓝和浅蓝夹着的地平线.今晚的月亮圆得像人造光源,与机翼的指示灯一同照亮了夜幕.​......
  • 关于Copilot出现:You don`t have access to Github Copilot .....的问题解决方案
    前面如何如何配置,以及如何如何上传学生证资料等我这里不赘述badendinghappyending出现这个界面这个问题就是set_up不是很完全,设置一下就行disable改为enable等这样再回去IDE,就可以正常使用了......
  • 牛客周赛 Round 65 题解
    牛客周赛Round65牛客周赛Round65A-超市_牛客周赛Round65#include<bits/stdc++.h>#defineendl'\n'usingnamespacestd;voidsolve(){ intn,a,b;cin>>n>>a>>b; intans=0; for(inti=0;i<=100;i++){ for(intj=0;j<=100;j++){......
  • P3780 苹果树 题解
    传送门夏天近了,又到了恋爱的季节,小Q家门前的苹果树上结满了红红圆圆的苹果。这株苹果树是一个有着\(n\)个结点的有根树,其中结点被依次编号为\(1\)至\(n\)。\(1\)号结点为根,其余每一个结点的父结点一定是某个编号较小的结点。每一个结点上都有一些苹果,第\(i\)个结点上有\(a_i(a_......
  • 【轰炸题解】
    轰炸题目描述“我该怎么办?”飞行员klux向你求助。事实上,klux面对的是一个很简单的问题,但是他实在太菜了。klux要想轰炸某个区域内的一些地方,它们是位于平面上的一些点,但是(显然地)klux遇到了抵抗,所以klux只能飞一次,而且由于飞机比较破,一点起飞就只能沿直线飞行,无法......
  • Codeforces Round 983 div2 个人题解(A~D)
    CodeforcesRound983div2个人题解(A~D)Dashboard-CodeforcesRound983(Div.2)-Codeforces火车头#define_CRT_SECURE_NO_WARNINGS1#include<algorithm>#include<array>#include<bitset>#include<cassert>#include<cmath>#in......