1. 程式人生 > 實用技巧 >Day 11.25模擬賽遊記

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\)們應該,不肯定能很快寫完的!