【2020.11.25提高组模拟】树的解构(deconstruct) 题解

【2020.11.25提高组模拟】树的解构(deconstruct) 题解

题目描述

给一棵以\(1\)为根的外向树,进行\((n-1)\)次删边操作,每次都会从没有删掉的边中等概率地删掉一条边\(a\to b\),代价是\(b\)的子树大小。删去这条边后\(b\)为根的子树会形成一棵新的以\(b\)为根的有根外向树。求进行\(n-1\)次操作后期望的代价总和是多少,对\(10^9+7\)取模。

\(n\le2\times10^6\)

Solution

题解中有两个解决方案,我暂且只看懂了\(\texttt{Alternative Solution}\),就先记录这种吧。

对每个点x考虑计算贡献。当\(x\)会对除了\(x\)自身外的\(x\)的祖先\(y\)产生贡献\(1\)的情况当且仅当\(y\to x\)方向上的\(path_{y\to son_y}\)\(path_{x\to y}\)内所有边中第一个删去,而这种情况的概率为\(\frac1{dis_{x\to y}}\)。每个\(x\)都有\(dep_x-1\)个这样的祖先\(y\)\(dis_{x\to y}\in [1,dep_x]\cup\mathbb{Z}\),所以点\(x\)的贡献就是

\[\sum_{i=1}^{dep_x}\frac1i \]

所以

\[ans=\sum_{i=1}^n\sum_{j=1}^{dep_i}\frac1j \]

线性(或加个\(\log\)也行)预处理出\(\frac1j\)的前缀,然后\(O(n)\)\(dep\)即可。

注意别用\(dfs\),会爆栈\(RE\)

Code

直接放线性求逆元好了。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#ifdef TH
#define debug printf("Now is %d\n",__LINE__);
#else
#define debug
#endif
using namespace std;

template<class T>inline void read(T&x)
{
    char ch=getchar();
    int fu;
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    x*=fu;
}
inline LL read()
{
	LL x=0,fu=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
    int g=0;
    if(x<0) x=-x,putchar('-');
    do{G[++g]=x%10;x/=10;}while(x);
    for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int n;
int head[2000010],ver[2000010],nxt[2000010];
int cnt;
int depth[2000010],fa[2000010];
void insert(int x,int y)
{
	nxt[++cnt]=head[x];
	ver[cnt]=y;
	head[x]=cnt;
}
void dfs(int x)
{
	for(int i=head[x];i;i=nxt[i])
	{
		dfs(ver[i]);
		depth[ver[i]]=depth[x]+1;
	}
}
void bfs()//出题人不讲武德 dfs怎么了? 惹你了? (doge)
{
	queue<int>q;
	q.push(1);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=nxt[i]) q.push(ver[i]),depth[ver[i]]=depth[x]+1;
	}
}
#define p 1000000007
LL inv[2000010],sum[2000010];
IL LL qpow(LL a,LL b)
{
	LL ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}
LL ans;
int main()
{
//	freopen("deconstruct.in","r",stdin);
//	freopen("deconstruct.out","w",stdout);
	n=read();
//	for(re int i=1;i<=n;i++) sum[i]=(qpow(i,p-2)+sum[i-1])%p;
	inv[0]=inv[1]=sum[1]=1;
	for(re int i=2;i<=n;i++)
	{
		inv[i]=(p-p/i)*inv[p%i]%p;
		sum[i]=sum[i-1]+inv[i];
	}
	for(re int i=2;i<=n;i++) insert(fa[i]=read(),i);
	bfs();
	for(re int i=1;i<=n;i++) ans=(ans+sum[depth[i]])%p;
	write(ans);
	return 0;
}

Upd

发生了一件玄学的事情。

\(O(n)\):

image-20201125170951986

image-20201125171138578

image-20201125171210151

image-20201125193800601

\(O(n\log n)\):

image-20201125171059500

image-20201125171238751

image-20201125193820081

两份代码都在上面,\(O(n\log n)\)就是上面的注释部分。

玄学!

Upd-2

现在都过不去了\(\dots\)

展示一份过了的代码吧,但是不保证过。\(jz\)的评测机有点累了啊……

#include<cstdio>
#define mod 1000000007
using namespace std;
int n,dep[2000010],to[2000010],nxt[2000010]; 
long long ans,kpl[2000010]={0,1},q[2000010]={0,1};
int cnt,h[2000010];
int l=1,r=2;
int res;
char ch;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
inline int read() {
	res=0;
	ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
	return res;
}
inline void add(int x,int y)
{
	to[++cnt]=y;
	nxt[cnt]=h[x];
	h[x]=cnt;
}
int i,x;
int main()
{
	freopen("deconstruct.in","r",stdin);
	freopen("deconstruct.out","w",stdout);
	n=read();
	for(i=2;i<=n;i++)
	{
		add(read(),i);
		kpl[i]=kpl[mod%i]*(mod-mod/i);
		if(kpl[i]>=mod) kpl[i]%=mod;
	}
	for(i=2;i<=n;i++) 
	{
		kpl[i]+=kpl[i-1];
		if(kpl[i]>=mod) kpl[i]-=mod;
	}
	while(r-l)
	{
		x=q[l++];
		for(i=h[x];i;i=nxt[i])
		{
			dep[to[i]]=dep[x]+1;
			q[r++]=to[i];
		}
	}
	for(i=1;i<=n;i++) 
	{
		ans+=kpl[dep[i]];
	}
	printf("%lld",ans%mod);
	return 0;
}

image-20201125195945654

posted @ 2020-11-25 16:42  Vanilla_chan  阅读(162)  评论(0)    收藏  举报