LG模拟赛#2 T3 搬题

本文探讨了一种树形结构中求解最大路径的问题,出题人希望通过重新分配题目,使得节点间距离最大化以降低重复题目的嫌疑。通过寻找树的重心并计算所有节点到重心的距离之和,得出最优解。同时,文章介绍了在最大路径确定后,计算不同搬题方案数量的方法,利用容斥原理和组合数学进行优化。最后,提供了C++代码实现这一过程。
摘要由CSDN通过智能技术生成

正题

可怜的出题人(跟九条可怜没有关系)要给 n n n 个地方出题。

但是出题人太累了,他决定把以前给这些地方出过的题重新搬一搬。

n n n 个地方以 1 , … , n 1,\dots,n 1,,n 编号。出题人总结出了他们之间的联系,是一个树形。如果出题人把以前给第 i i i 个地方出的题搬到第 j j j 个地方,那么他希望 i i i j j j 在这棵树上的距离越长越好。

于是出题人需要决定一个排列 p 1 , … , p n p_1,\dots,p_n p1,,pn ,要求最大化
S = ∑ i = 1 n d i s ( i , p i ) S=\sum_{i=1}^n \mathrm{dis}(i,p_i) S=i=1ndis(i,pi)
这个 S S S 越大,出题人可能受到的损失(被指出是原题于是被上某乎或扣工资等)就越小。
有时候出题人还想知道在最大化 S S S 的前提下自己有多少种搬题的方案。即有多少排列 p 1 , … , p n p_1,\dots,p_n p1,,pn,使得 S S S 最大。
由于这个数可能非常大,你只需要输出其对 998244353998244353 取模的结果。

首先考虑如何最大化 S S S
我们只需要找到重心作为根,求出 2 ∗ ∑ i = 1 n d e p i 2*\sum_{i=1}^n dep_i 2i=1ndepi即为答案,不会有更大的答案,因为可以对于每一条边考虑,贡献的权值最多为 2 ∗ min ⁡ ( s z [ x ] , n − s z [ x ] ) 2*\min (sz[x],n-sz[x]) 2min(sz[x],nsz[x]),其中该条边所连接的远根点为 x x x,在上面的方案中达到了最大值。

方案数显然就是要在重心为根时,将重心的每一个儿子的子树点放到其他的儿子子树中。
显然可以容斥,记 S S S为选择放在当前子树的集合, a i a_i ai表示第 i i i棵子树有多少个选择放在当前子树, s i s_i si表示第 i i i棵子树有多少个点。
则有答案式子 ∑ S ( − 1 ) ∣ S ∣ ( n − ∣ S ∣ ) ! ∏ i = 1 m C s i a i P s i a i \sum_{S} (-1)^{|S|} (n-|S|)!\prod_{i=1}^m C_{s_i}^{a_i}P_{s_i}^{a_i} S(1)S(nS)!i=1mCsiaiPsiai
后面使用Dp或者NTT来优化即可。

#include<bits/stdc++.h>
using namespace std;

const int N=200010,mod=998244353;
struct edge{
	int y,nex;
}s[N<<1];
int first[N],len=0,n,t,sz[N],mmin=1e9,pos=0,mmax[N],fac[N],inv[N];
int f[N];

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;
}

int ksm(int x,int t){
	int tot=1;
	while(t){
		if(t&1) tot=1ll*tot*x%mod;
		x=1ll*x*x%mod;
		t/=2;
	}
	return tot;
}

void dfs(int x,int fa){
	sz[x]=1;
	for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa){
		dfs(s[i].y,x);
		sz[x]+=sz[s[i].y];
		mmax[x]=max(mmax[x],sz[s[i].y]);
	}
	if(max(mmax[x],n-sz[x])<mmin) mmin=max(mmax[x],n-sz[x]),pos=x;
}

long long tt=0;

void dfs_2(int x,int fa,int dep){
	sz[x]=1;tt+=dep;
	for(int i=first[x];i!=0;i=s[i].nex) if(s[i].y!=fa){
		dfs_2(s[i].y,x,dep+1);
		sz[x]+=sz[s[i].y];
	}
}

int down(int x,int y){
	return 1ll*fac[x]*inv[x-y]%mod;
}

int main(){
	scanf("%d %d",&n,&t);
	int x,y;
	for(int i=1;i<n;i++) scanf("%d %d",&x,&y),ins(x,y),ins(y,x);
	dfs(1,0);dfs_2(pos,0,0);printf("%lld\n",tt*2);if(t==1) return 0;
	fac[0]=1;for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
	inv[n]=ksm(fac[n],mod-2);for(int i=n-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
	int tot=0;f[0]=1;
	for(int i=first[pos];i!=0;i=s[i].nex){
		int m=sz[s[i].y];
		for(int j=tot;j>=0;j--)
			for(int k=m;k>=1;k--)
				f[j+k]=(f[j+k]+1ll*f[j]*down(m,k)%mod*down(m,k)%mod*inv[k])%mod;
		tot+=m;
	}
	int ans=0,t=1;
	for(int j=0;j<=tot;j++)
		ans=(ans+1ll*t*fac[n-j]%mod*f[j])%mod,t=(t==1?mod-1:1);
	printf("%d\n",ans);
}
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值