P14467 [COCI 2025/2026 #1] 扔球 / Krugomet 题解

题目链接

我的博客

思路

这道题,\(k\) 的取值范围很大,我们考虑倍增。

\(f_{i,j}\) 表示第 \(i\) 个人,经过 \(2^j\) 轮,最终扔给哪个人。然后递推式即为

\[f_{i,j}=f_{f_{i,j-1},j-1} \]

表示含义为:第 \(i\) 个人,经过 \(2^j\) 轮扔给谁是经过 \(2^{j-1}\) 轮之后再经过 \(2^{j-1}\) 扔给谁。

时间复杂度 \(O(n \log k)\)

警示后人

  • 如果你经常写 \(10^5\) 的倍增,请注意:本题 \(k\) 可以取到 \(10^9\)。因此 \(\log_2 10^9\) 的取值接近 \(30\)

代码

const int N=4e5+10;
int n,k,a[N];
ll f[N][50];//同上
ll ans[N];//答案
int findf(int x,int k){//倍增找 x 经过 k 轮后的“祖先”
	for(int j=31;j>=0;j--){
		if(k&(1<<j)){
			x=f[x][j];
		}
	}
	return x;
}
signed main(){
	n=Read();k=Read();
	for(int i=1;i<=n;i++) a[i]=Read();
	for(int i=1;i<=n;i++){
		int x=Read();
		f[i][0]=x;//直接“父节点”即为暗恋对象
	}
    //倍增的预处理
	for(int j=1;j<=31;j++){
		for(int i=1;i<=n;i++){
			f[i][j]=f[f[i][j-1]][j-1];
		}
	}
	for(int i=1;i<=n;i++){
		int fa=findf(i,k);
		ans[fa]+=a[i];
	}
	ll mx=0;
	for(int i=1;i<=n;i++){
		mx=max(mx,ans[i]);
	}
	printf("%lld\n",mx);
	for(int i=1;i<=n;i++){
		if(mx==ans[i]) printf("%lld ",i);
	}
	return 0;
}
posted on 2025-11-10 11:01  _Liuliuliuliuliu  阅读(0)  评论(0)    收藏  举报