Day 11.25模拟赛游记

又是测数据的一天?不知情的我又爆零了。


T2.树的解构(deconstruct)

题目大意:给定\(1\)为根,随机选择一条边删去,删去的代价为这条边所指向的子节点的子树大小,求删去\(n-1\)条边时的期望。

因为整棵子树计算会有子孙节点是否被删除过,所以我们可以只计算当前这个点的贡献,而不是这棵子树的。

对于当前节点肯定只有到根节点的路径上的边有贡献,所以其余边的方案数为\(C_{n-1}^{dep_u}\times (n-dep_u-1)!\),然后计算贡献就从一棵树变成了一条链。

我们现在来考虑一条链的情况,因为我们要计算贡献的点只是链的末尾,所以枚举贡献的多少,然后计算即可,要考虑我们计算的是所有方案的贡献,所以贡献还要乘以当前的方案数,可证明为第二类斯特林数,递推公式为\(f_i=f_{i-1}*i+(i-1)!\),先预处理好,然后\(O(n)\)计算即可。

手起码落,把这题咔嚓了:

#include<bits/stdc++.h>
#define re register
#define mod 1000000007
using namespace std;
inline int read()
{
	re int x=0,f=1;
	re char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') f*=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return x*f;
}
const int N=2000005;
inline long long Pow(long long x,int y)
{
	re long long sum=1;
	for(;y;y>>=1,x=x*x%mod)
		if(y&1) sum=sum*x%mod;
	return sum;
}
struct edge{int v,net;}e[N];
int n,cnt,hd[N],dep[N];
long long ans,jc[N]={1},inv[N]={1},f[N];
queue <int> q;
inline void add(int u,int v){e[++cnt].v=v,e[cnt].net=hd[u],hd[u]=cnt;}
void first()
{
	q.push(1);
	for(re int u;!q.empty();q.pop())
	{
		u=q.front();
		for(re int i=hd[u],v;i;i=e[i].net)
		{
			v=e[i].v;
			dep[v]=dep[u]+1;
			q.push(v);
		}
	}
}
inline long long C(int m,int n){return jc[m]*inv[n]%mod*inv[m-n]%mod;}
int main()
{
	freopen("deconstruct.in","r",stdin);
	freopen("deconstruct.out","w",stdout);
	scanf("%d",&n);
	for(re int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod,inv[i]=Pow(jc[i],mod-2);
	for(re int i=2;i<=n;i++) add(read(),i);
	f[1]=1;for(re int i=1;i<=n;i++) f[i]=(f[i-1]*i+jc[i-1])%mod;
	first();
	for(re int i=1;i<=n;i++)
		ans=(ans+C(n-1,dep[i])*jc[n-dep[i]-1]%mod*f[dep[i]]%mod)%mod;
	printf("%lld",ans*inv[n-1]%mod);
	return 0;
}

T3.小T与灵石(stone)

题目大意:一棵\(n\)个结点的有根树,树的根为\(1\)。一共有\(q\)次操作,第\(i\)次操作选定\(k_i\)个点\(p_1\),\(p_2\),...,\(p_{k_i}\),对每个点\(x\)定义\(f_{x,i}=max^{k_i}_{j=1}\)\(dist(x,pj)\),其中\(dist(x,y)\)表示树上\(x\),\(y\)两点间最短路经过的边数。再对每个点\(x\)定义\(g_x=min^{q}_{i=1}(f_x,i)\),对于每个点你需要求出\(g_x\)

题目可以简化一下,大概可以想得出是在给出的点的直径上乱搞,因为要找的时最长的路径,所以一定是直径上的端点作为路径上的端点,又因为要找更长的那一个,所以我们只要找所有点到直径的中点就行了,然后再加上直径长度的一半就行

要注意的是,可能会出现中点在边上的情况,这是我们就可以建一个虚点,但要注意,虚点与其他点计算长度为\(\frac{1}{2}\)(卡在这好久),然后换根DP就行了。

悲惨,代码就不放了,\(dalao\)们应该,不肯定能很快写完的!

posted @ 2020-11-25 21:58  Chester1011  阅读(88)  评论(0编辑  收藏  举报
/*simplememory*/