【JZOJ A组】【NOIP2019模拟2019.9.11】树堆(treap)

题目

【问题描述】
小 D 有一棵 n 个节点的树,并给了每个节点一个 1 ∼ n 的编号。所有节点的编
号是互不相同的。
由于小 D 最近对堆十分感兴趣,所以他希望研究这棵树节点的堆性质。由于小 D
比较小,所以他研究的是小根堆。
首先,小 D 选择了一个节点作为树的根。接下来对于树的每个节点,如果它的编
号是以它为根的子树中最小的,那么小 D 就称这个节点满足堆性质。
由于小 D 之前分配编号是比较随意,所以可能存在不满足堆性质的节点。
设此时树中满足堆性质的节点有 k 个,小 D 认为这样整棵树的权值是 pk,其中
p 是一个小 D 预先设置好的常数。
现在假设小 D 是从 n! 种方案中等概率随机一种编号的方案,然后从 n 种方案中
等概率随机一种选择根的方案,那么树的权值的期望是多少?
可以证明期望一定是一个分数 xy (gcd(?, ?) = 1),你只需要求出这个分数模
998244353 的结果:x ⋅ y−1 mod 998244353 = x ⋅ ?
998244351 mod 998244353。
【输入格式】
从输入文件 treap.in 中读入数据。
第一行两个整数 n, p,分别表示树的大小和小 D 设置的常数。
接下来 n − 1 行,每行两个整数,表示树的一条边。
【输出格式】
输出到文件 treap.out 中。
输出一行一个整数,表示答案。
【样例 1 输入】
18 1
1 2
1 3
1 4
1 16
2 5
3 6
4 7
2019 年非专业级软件能力认证提高级(第二轮) 第二次认证 树堆(treap) 第 8 页 共 8 页
4 12
4 17
5 8
6 9
6 11
6 13
7 10
7 14
9 15
15 18
【样例 1 输出】
1
【样例 1 解释】
对任意的树,由于 pk = 1k = 1,所以权值都是 1,期望权值自然也是 1。
【样例 2 输入】
3 2
1 2
1 3
【样例 2 输出】
776412279
【样例 2 解释】
在所有方案中,权值为 21 有 4 种,权值为 22 有 10 种,权值为 23 有 4 种。
所以总权值为 80,期望权值为 80
18

40
9 ,模意义下为 776412279。
【样例 3】
见选手目录下的 treap/treap3.in 与 treap/treap3.ans。
【子任务】
对于 10% 的测试点,保证 n ≤ 10。
对于 20% 的测试点,保证 n ≤ 18。
对于 30% 的测试点,保证 n ≤ 100。
对于 50% 的测试点,保证 n ≤ 1000。
对于另 10% 的测试点,保证 p ≤ 1。
对于另 10% 的测试点,保证树是一条链。
对于另 10% 的测试点,保证树上存在一个点与其他点均相连。
对于 100% 的测试点,保证 1 ≤ n ≤ 106, 0 ≤ p < 998244353。

思路

考虑固定根的情形。由 [CTS2019]氪金手游 的经验,可以得到:每个点是否满足堆性质这个事件,是互相独立的。且概率等于以它为根的子树的倒数。
于是就可以进行 DP 了: πchild(x) (size(x)+p-1)/size(x)。

对于根可变的情形,相当于对所有根求出期望后然后取平均值。因此,我们只需枚举所有可能的根分别DP,最后取平均值即可。

但是太慢了

注意到式子的形式,是较为容易进行换根 DP 的,因此只需再次 dfs 来实现一个换根 DP 即可。

但是只有70分

这是因为(size(x)+p-1)/size(x) 在模意义下可以等于 。
因此我们需要实现一些珂技,比如前缀后缀积神马的。
不过这里并不需要这么麻烦。由于全都是乘积式,因此只需要维护零因子的个数 (即标准分解式中mod的指数) 以及剩下部分的值就可以了。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+77,mod=998244353;
int ls[N],s[N],n,cnt,q[N],nq;
ll h[N],f[N],g[N],p,invs[N],pre[N];
struct E
{
	int to,next;
}e[N*2];
void add(int u,int v)
{
	e[++cnt].to=v; e[cnt].next=ls[u]; ls[u]=cnt;
}
inline ll ny(ll x)
{
	ll ass=1;
	for(int y=mod-2; y; y>>=1,x=x*x%mod) if(y&1) ass=ass*x%mod;
	return ass;
}
inline ll calc(int s)
{
	return (p*invs[s]+1-invs[s]+mod)%mod;
}
void dfs1(int u,int fa)
{
	s[u]=1,h[u]=1;
	for(int i=ls[u]; i; i=e[i].next)
		if(e[i].to!=fa)
		{
			int v=e[i].to;
			dfs1(v,u);
			s[u]+=s[v];
			h[u]=h[u]*f[v]%mod;
		}
	f[u]=h[u]*calc(s[u])%mod;
}
ll ans;
void dfs2(int u,int fa)
{
	ans=(ans+h[u]*g[u]%mod*calc(n))%mod;
	pre[q[nq=0]=0]=1;
	for(int i=ls[u]; i; i=e[i].next)
		if(e[i].to!=fa)
		{
			int v=e[i].to;
			pre[v]=pre[q[nq]]*f[v]%mod;
			q[++nq]=v;
		}
	ll suc=1;
	for(int i=nq; i>=1; i--)
	{
		g[q[i]]=pre[q[i-1]]*suc%mod*g[u]%mod*calc(n-s[q[i]])%mod;
		suc=suc*f[q[i]]%mod;
	}
	for(int i=ls[u]; i; i=e[i].next)
		if(e[i].to!=fa)
		{
			int v=e[i].to;
			dfs2(v,u);
		}
}
int main()
{
	freopen("treap.in","r",stdin); freopen("treap.out","w",stdout);
	scanf("%d%d",&n,&p);
	invs[1]=1;
	for(int i=2; i<=n; i++) invs[i]=(mod-mod/i)*invs[mod%i]%mod;
	for(int i=1,x,y; i<n; i++)
	{
		scanf("%d%d",&x,&y); add(x,y); add(y,x);
	}
	dfs1(1,0);
	g[1]=1; dfs2(1,0);
	ans=ans*ny(n)%mod;
	printf("%lld",ans);
}
发布了703 篇原创文章 · 获赞 392 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/Eric1561759334/article/details/100786259
今日推荐