[Gym-101234D]Forest Game

壹、题目描述

传送门

贰、题解

在期望 \(\tt DP\) 专题,已经对此题思路进行过说明了(如果要看就去这里的典例营),定义 \(\text{dis}(u, v)\)\(u,v\) 之间的距离(边数),那么最后的答案就是

\[\sum_{u}\sum_v{1\over \text{dis}(u,v)+1} \]

不难发现这个东西,两点间真正的距离是点的个数,赶脚有点难搞,但是我们可以按照边数计算,最后,有 \(i\) 条边的路径实际上对应的 \(i+1\) 个点的数量,进行这个转换之后,我们就可以直接上淀粉质了,淀粉质代码简易,思路清晰,不会真有人打边分治吧?淀粉质还是使用简易容斥,用随意匹配的方案减去单个子树自己匹配自己的方案数。

但是需要注意一个细节,由于 \(u\) 可以等于 \(v\),所以我们统计的时候应该还要算 \(0\) 条边的情况 —— 这个情况对应的就是有一个点的路径。

由于 \(\bmod=10^9+7\),我们可以 把出题人吊起来打 (MTT 常数太大了,我们不考虑那东西) 使用 \(\tt FFT\) 暴力算,最后再取模。

实际上我最开始打的是 MTT,但是常数太......

叁、参考代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
#define Endl putchar('\n')

typedef long long ll;
const int maxn=3e5+10;
const int mod=1e9+7;
const double pi=acos(-1.0);

inline int qkpow(int a, int n){
	int ret=1;
	for(; n>0; n>>=1, a=1ll*a*a%mod)
		if(n&1) ret=1ll*ret*a%mod;
	return ret;
}

struct cplx{
	double x, y;
	cplx(){}
	cplx(const double X, const double Y): x(X), y(Y){}
	inline cplx operator +(const cplx rhs) const{return cplx(x+rhs.x, y+rhs.y);}
	inline cplx operator -(const cplx rhs) const{return cplx(x-rhs.x, y-rhs.y);}
	inline cplx operator *(const cplx rhs) const{return cplx(x*rhs.x-y*rhs.y, x*rhs.y+y*rhs.x);}
};

// should prepare first
namespace poly{
	int rev[maxn<<1], n;
	inline void prepare(const int len){
		for(n=1; n<len; n<<=1);
		for(int i=0; i<n; ++i)
			rev[i]=(rev[i>>1]>>1)|((i&1)?(n>>1):0);
	}
	inline void fft(cplx* f, const int opt){
		for(int i=0; i<n; ++i) if(i<rev[i])
			swap(f[i], f[rev[i]]);
		for(int p=2; p<=n; p<<=1){
			int len=p>>1;
			cplx buf=cplx(cos(pi/len), opt*sin(pi/len));
			for(int k=0; k<n; k+=p){
				cplx w=cplx(1, 0), tmp;
				for(int i=k; i<k+len; ++i, w=w*buf){
					tmp=f[i+len]*w;
					f[i+len]=f[i]-tmp;
					f[i]=f[i]+tmp;
				}
			}
		}
	}
	cplx a[maxn<<1];
	inline void pow(ll* f){
		for(int i=0; i<n; ++i)
			a[i]=cplx(f[i], 0);
		fft(a, 1);
		for(int i=0; i<n; ++i)
			a[i]=a[i]*a[i];
		fft(a, -1);
		for(int i=0; i<n; ++i)
			f[i]=(ll)(a[i].x/n+0.5);
	}
}

int n;

struct edge{
	int to, nxt;
	edge(){}
	edge(const int T, const int N): to(T), nxt(N){}
}e[maxn<<1];
int tail[maxn], ecnt;
inline void add_edge(const int u, const int v){
	e[ecnt]=edge(v, tail[u]); tail[u]=ecnt++;
	e[ecnt]=edge(u, tail[v]); tail[v]=ecnt++;
}

inline void input(){
	memset(tail, -1, sizeof tail);
	scanf("%d", &n);
	int u, v;
	for(int i=1; i<n; ++i){
		scanf("%d %d", &u, &v);
		add_edge(u, v);
	}
}

int used[maxn+5];
int siz[maxn+5], f[maxn+5];
void findrt(const int u, const int par, const int n, int& rt){
	siz[u]=1, f[u]=0;
	for(int i=tail[u], v; ~i; i=e[i].nxt)
		if(!used[v=e[i].to] && v!=par){
			findrt(v, u, n, rt);
			f[u]=max(f[u], siz[v]);
			siz[u]+=siz[v];
		}
	f[u]=max(f[u], n-siz[u]);
	if(f[u]<f[rt]) rt=u;
}
void build(int, const int);
ll ans[maxn<<1], t1[maxn<<1], t2[maxn<<1];
int maxl=0;
void dfs(const int u, const int par, const int curl){
	siz[u]=1;
	maxl=max(maxl, curl);
	++t2[curl];
	for(int i=tail[u], v; ~i; i=e[i].nxt)
		if(!used[v=e[i].to] && v!=par)
			dfs(v, u, curl+1), siz[u]+=siz[v];
}
void calc(const int u){
	int len=0;
	siz[u]=1;
	++t1[0];
	for(int i=tail[u], v; ~i; i=e[i].nxt)
		if(!used[v=e[i].to]){
			maxl=0;
			dfs(v, u, 1), siz[u]+=siz[v];
			len=max(len, maxl);
			for(int i=0; i<=maxl; ++i)
				t1[i]+=t2[i];
			poly::prepare((maxl+1)<<1);
			poly::pow(t2);
			for(int i=0; i<=(maxl<<1)+1; ++i)
				ans[i]-=t2[i];
			memset(t2, 0, (maxl*2+5)<<3);
		}
	poly::prepare((len+1)<<1);
	poly::pow(t1);
	for(int i=0; i<=(len<<1); ++i)
		ans[i]+=t1[i];
	memset(t1, 0, (len*2+5)<<3);
}
void divide(const int u){
	used[u]=1; calc(u);
	for(int k=tail[u], v; ~k; k=e[k].nxt)
		if(!used[v=e[k].to])
			build(v, siz[v]);
}
void build(int u, const int n){
	f[0]=n; int rt=0;
	findrt(u, 0, n, rt);
	divide(rt);
}

signed main(){
	input();
	build(1, n);
	int res=0;
	for(int i=0; i<n; ++i)
		res=(res+ans[i]%mod*qkpow(i+1, mod-2)%mod)%mod;
	for(int i=1; i<=n; ++i)
		res=1ll*res*i%mod;
	printf("%d\n", res);
	return 0;
}

肆、用到の小 \(\tt trick\)

期望有很多种计算方法,有用 总体/总体 的,也有考察单个情况在什么时候会对期望产生贡献,显然此题使用的是后者。

另外,路径的组合也是一种类似卷积的东西,好多东西都是卷积啊......

posted @ 2021-03-09 22:21  Arextre  阅读(95)  评论(0编辑  收藏  举报