SCP2025T2:P14254 分割(divide) 题解

P14254 分割(divide)

题面上的 1 ≤ p i < i 1 \leq p_i < i 1≤pi<i 是不是有问题啊,应该是 1 ≤ p i ≤ i 1 \leq p_i \le i 1≤pi≤i 吧,定义为:第 i i i 个正整数表示结点 ( i + 1 ) (i+1) (i+1) 的父结点的编号 p i p_i pi。

思路

首先我们记以 i i i 为根的子树中深度最深的点的深度为 m i m_{i} mi,则 S i = [ d i , m i ] S_{i}=[d_{i},m_{i}] Si=[di,mi]。可以发现,如果 d b 1 < d b i ( 1 < i ≤ n ) d_{b_1}< d_{b_i}(1<i\le n) db1<dbi(1<i≤n),那么 d b 1 ∉ S j d_{b_1}\notin S_{j} db1∈/Sj,求交集后一定不会满足条件二。故 d b 1 = d b 2 = ⋯ = d b k d_{b_1}=d_{b_2}=\dots=d_{b_k} db1=db2=⋯=dbk。

我们把深度相同的点放进桶里,针对同一深度的点,从小到大枚举 m i m_{i} mi 的值。设 c n t cnt cnt 为相等的 m i m_{i} mi 的个数, p p p 为比 m i m_{i} mi 小的值的的个数, s s s 为比 m i m_{i} mi 大的值的个数。显然,比 m i m_{i} mi 小的值必然不可能成为被挖去的子树,如果挖去,那么交集后一定不会满足条件二。我们首先钦定一个 m i m_{i} mi 为 b 1 b_1 b1,那么就是在剩下 s + c n t − 1 s+cnt-1 s+cnt−1 个子树中选择 k − p − 1 k-p-1 k−p−1 个挖去,剩下的保留。有问题吗?当然有,如果所有相等的 m i m_{i} mi 都没有被挖去,且至少有一个比 m i m_{i} mi 大的没有被挖去,那么交集就不满足条件二了,减去这种情况即可。

代码

时间复杂度 O ( n log ⁡ M ) O(n\log_{}{M} ) O(nlogM),其中 M = 998244353 M=998244353 M=998244353,不过可以优化求逆元的复杂度可以得到 O ( n log ⁡ n ) O(n\log_{}{n} ) O(nlogn),懒得改了。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5,mod=998244353;
int n,k,dep[N],m[N],jie[N],ni[N];
vector<int> a[N],t[N];
void dfs(int x)
{
	m[x]=dep[x];
	for(int i=0;i<a[x].size();i++)
	{
		dep[a[x][i]]=dep[x]+1;
		dfs(a[x][i]);
		m[x]=max(m[x],m[a[x][i]]);
	}
	t[dep[x]].push_back(m[x]);
}
int ksm(int x,int y)
{
	if(y==1) return x;
	long long mid=ksm(x,y>>1);
	if(y&1) return mid*mid%mod*x%mod;
	else return mid*mid%mod;
}
int C(int n,int m)
{
	if(m==0) return 1;
	if(n<m||n<0||m<0) return 0;
	return 1ll*jie[n]*ni[n-m]%mod*ni[m]%mod;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>k;
	for(int i=2;i<=n;i++)
	{
		int p;
		cin>>p;
		a[p].push_back(i);
	}
	jie[0]=1;
	for(int i=1;i<=n;i++)
		jie[i]=1ll*jie[i-1]*i%mod,ni[i]=ksm(jie[i],mod-2);
	//直接用的快速幂求逆元,时间稍微多些
	dep[1]=1;
	dfs(1);
	long long ans=0;
	for(int i=2;i<=n;i++)
	{
		sort(t[i].begin(),t[i].end());
		for(int j=0;j<t[i].size();)
		{
			int cnt=0,p=j;
			for(int d=j;d<t[i].size();d++)
				if(t[i][d]==t[i][j]) cnt++;
				else break;
			int s=t[i].size()-j-cnt;
			long long sum=C(s+cnt-1,k-1);
			if(s>k-1)sum=(sum-C(s,k-1))%mod;//在s中取k-1个挖去,剩下的给根节点,保证至少给根节点一个
			ans=(ans+sum*cnt%mod)%mod;//任意一个都可以当b1
			j+=cnt;
		}
	}
	ans=ans*jie[k-1]%mod;//序列b有序,要乘上排列数
	ans=(ans+mod)%mod;//涉及减法,可能出现负数
	cout<<ans;
	return 0;
}
相关推荐
心中有国也有家31 分钟前
hccl 架构拆解:昇腾集合通信库到底在做什么?
人工智能·经验分享·笔记·分布式·算法·架构
小O的算法实验室1 小时前
2026年MCS,Q-learning增强MOPSO与改进DWA融合算法+复杂三维地形下特定移动机器人动态路径规划
算法
Peter·Pan爱编程2 小时前
10. new_delete 不是 malloc_free 的包装
c++·人工智能·算法
故事和你913 小时前
洛谷-【动态规划1】动态规划的引入2
开发语言·数据结构·c++·算法·动态规划·图论
重生之我是Java开发战士3 小时前
【动态规划】背包问题:完全背包,二位费用的背包问题,似包非包
算法·动态规划
LabVIEW开发4 小时前
LabVIEW实现FDTD 电磁仿真
算法·labview·labview知识·labview功能·labview程序
Together_CZ4 小时前
DTSemNet :Vanilla Gradient Descent for Oblique Decision Trees——用于倾斜决策树的普通梯度下降
算法·决策树·机器学习·vanilla·gradient·dtsemnet·用于倾斜决策树的普通梯度
一条大祥脚4 小时前
ABC459 贪心构造|树形DP|组合数学|贪心|单调栈|势能|前缀和
算法·深度优先
灰灰勇闯IT5 小时前
DeepEP:MoE 推理的 AllToAll 通信瓶颈怎么解
算法·cann
一行代码一行诗++5 小时前
goto语句
java·开发语言·算法