#C241114E. 树上宝藏


#C241114E. 树上宝藏

标签(Label)

  • 概率期望
  • 数学
  • 贪心

网址(Website)

G - Treasure Hunting (atcoder.jp)

题目详情 - 树上宝藏 - Super

题目(Problem)

问题陈述

有一棵有根的树,树上有 $N + 1$ 个顶点,编号从 $0$ 到 $N$ 。顶点 $0$ 是根,顶点 $i$ 的父顶点是顶点 $p_i$ 。
顶点 $1$ 、顶点 $2$ 、…、顶点 $N$ 中的一个顶点藏有宝藏。宝藏位于顶点 $i$ 的概率是 $\frac{a_i}{\sum_{j=1}^N a_j}$ 。此外,每个顶点都处于两种状态之一:”搜索 “和 “未搜索 “两种状态之一。最初,顶点 $0$ 被搜索,其他所有顶点都未被搜索。
在包含宝藏的顶点被搜索到之前,执行以下操作:

  • 选择一个父顶点已被搜索的未搜索顶点,并将其标记为已搜索。

求在最小化预期操作数的情况下所需的预期操作数,模为 $998244353$ 。

给你 $T$ 个测试案例,求解每个案例。

限制因素

  • $1 \leq T \leq 2 \times 10^5$
  • $1 \leq N \leq 2 \times 10^5$
  • $0 \leq p_i < i$
  • $1 \leq a_i$
  • $\sum_{i=1}^N a_i \leq 10^8$
  • 所有测试用例中 $N$ 的总和最多为 $2 \times 10^5$ 。
  • 所有输入值均为整数。

输入

输入信息按以下格式从 “标准输入”(Standard Input)中输入。这里, $\mathrm{case}_i$ 表示第 $i$ 测试用例。

$T\\
\mathrm{case}_1\\
\mathrm{case}_2\\
\vdots\\
\mathrm{case}_T\\$
每个测试用例的格式如下:

$N$
$p_1$ $p_2$ $\dots$ $p_N$
$a_1$ $a_2$ $\dots$ $a_N$

输出

打印 $T$ 行。第 $i$ 行应包含第 $i$ 测试用例的答案。

样例 #1

样例输入 #1

3
3
0 0 1
1 2 3
5
0 1 0 0 0
8 6 5 1 7
10
0 1 1 3 3 1 4 7 5 4
43 39 79 48 92 90 76 30 16 30

样例输出 #1

166374061
295776107
680203339

在第一个测试用例中,预期操作数为 $\frac{13}{6}$ 。

题解(Solution)

$\qquad$我们假设没有“子节点必须在父节点之后”的限制,那么这道题就变成了安排一种顺序使得最后得到宝藏的期望最小,很明显这个地方我们肯定先去选择有宝藏概率更大的点。

$\qquad$为了方便表示,我们记录 $p_i = \frac{a_i}{\sum_{i=1}^N a_i}$ (其实就是概率),那么最后的答案就是操作期望步数乘上发生概率即 $\sum_{i=1}^N i\times p_i$ ,那么现在就只需要考虑对于父亲的限制了,由于父亲只能先访问,我们试着推一下

代码(Code)

#include<bits/stdc++.h>
#include<vector>
#include<queue>
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Rof(i,l,r) for(int i=l;i>=r;i--)
using namespace std;
#define P pair<int,int> 
#define int long long
#define x first 
#define y second
#define in(x,l,r) (l<=x && x<=r)
inline int rd(){int x;return cin>>x,x;}
const int inf = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const int N = 1012345;
bool ST;
inline void add(int &x,int y){if((x+=y)>=mod) x-=mod;}
inline void mul(int &x,int y){x=x*y%mod;}
inline int inv(int x,int k=mod-2){
	int res=1;while(k){
		if(k&1) mul(res,x);
		mul(x,x), k>>=1;
	}return res;
}

int n,sm,ans,a[N],f[N];

int fa[N];inline int find(int x){
	if(x==fa[x]) return x;
	return fa[x] = find(fa[x]);
}

struct Wolf{
	int val,siz,p,pos;
	Wolf operator+(const auto y)const{
		Wolf res;
		res.val = (val+y.val+siz*y.p)%mod;
		res.siz = siz+y.siz;
		res.p = (p+y.p)%mod;
		res.pos = pos;
		return res;
	}
	bool operator<(const Wolf y)const{
		return siz*y.p > y.siz*p; 
	}
}x[N];
priority_queue<Wolf> q;

bitset<N> vst;
void Solve(){
	sm=ans=0;
	
	cin>>n;For(x,1,n) f[x]=rd(), fa[x]=x;
	For(i,1,n) a[i]=rd(),sm += a[i];
	
	x[0] = {0,0,0,0};
	For(i,1,n) x[i] = {a[i], 1, a[i], i};
	For(i,1,n) q.emplace(x[i]), vst[i]=0;
	
	while(q.size()){
		auto w = q.top();q.pop();
		if(vst[w.pos] || w.pos==0) continue;
		vst[w.pos] = true;
		int F = find(f[w.pos]);
		fa[w.pos] = F;
		x[F] = x[F] + x[w.pos];
		q.emplace(x[F]);
	}cout<<x[0].val*inv(sm)%mod<<'\n'; 
}

bool ED;
signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cerr<<abs(&ST-&ED)/1024./1024.<<"MB\n";
	int Tt=rd();double Tim=clock();
	while(Tt--) Solve();
	return cerr<<"TIME:"<<(clock()-Tim)/CLOCKS_PER_SEC,0;
}

文章作者: WolfDeer
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WolfDeer !
  目录