• [冲刺国赛2022] 数叶子


    一、题目

    有一棵 \(n\) 个点的树,每条边被分配了一个在 \(1,2...m\) 中的整数作为编号,且这些编号两两不同。

    对于所有的标号方式,求出这个问题的答案的和:保留编号在 \([l,r]\)\(1\leq l\leq r\leq m\))的边构成的森林中,有多少个点是叶子。(在本题中,叶子的定义为有恰好一个相邻点的节点)

    \(n\leq 10^6,m\leq 10^9\)

    二、解法

    可以用简单的贡献法写出答案,我们枚举区间长度 \(i\) 和作为叶子贡献的点 \(u\)

    \[\sum _{i=1}^m (m-i+1)\sum_{u=1}^n d_u\cdot i\cdot A(m-i,d_u-1)\cdot A(m-d_u,n-1-d_u) \]

    把上面的式子变一下形,可以得到:

    \[\sum_{u=1}^n d_u\cdot A(m-d_u,n-1-d_u)\sum_{i=1}^m i\cdot A(m-i+1,d_u) \]

    所以问题变成了,对于每个度数 \(d\) 求出:

    \[\sum_{i=1}^m i\cdot A(m-i+1,d)=\sum_{i=1}^m i(m-i+1)^{\underline d} \]

    \(i\) 当成自变量,在脑海中展开上式,发现它是一个关于 \(m\)\(d+2\) 次多项式。因为 \(m<d\) 时点值为 \(0\),所以只需要暴力求出剩下的点值,然后用 \(O(n)\) 的拉格朗日插值法即可解决问题。


    组合意义保平安,我们把枚举区间和分配标号两部分放在一起计数。问题变成了,有 \(m+2\) 个点,要从中选出 \(d+2\) 个点,其中两个点代表了区间 \([l,r]\)(指定标号的左右两个点),再乘上分配标号的 \(d!\) 即可:

    \[\sum_{i=1}^m i\cdot A(m-i+1,d)={m+2\choose d+2}\cdot d! \]

    时间复杂度 \(O(n)\)

    #include <cstdio>
    const int M = 1000005;
    const int MOD = 1004535809;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,d[M],b[M],inv[M],a[M];
    int qkpow(int a,int b)
    {
    	int r=1;
    	while(b>0)
    	{
    		if(b&1) r=r*a%MOD;
    		a=a*a%MOD;
    		b>>=1;
    	}
    	return r;
    }
    signed main()
    {
    	freopen("leaf.in","r",stdin);
    	freopen("leaf.out","w",stdout);
    	n=read();m=read();
    	for(int i=1;i<n;i++)
    		d[read()]++,d[read()]++;
    	for(int i=1;i<=n;i++) b[d[i]]++;
    	//
    	inv[0]=inv[1]=1;
    	for(int i=2;i<=n+1;i++)
    		inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
    	a[0]=((m+2)*(m+1)>>1)%MOD;
    	for(int i=1;i<n;i++)
    		a[i]=a[i-1]*(m+1-i)%MOD*inv[i+2]%MOD;
    	//
    	int f1=1,f2=1,ans=0;
    	for(int i=0;i<n-1;i++) f2=f2*(m-i)%MOD;
    	for(int i=1;i<n;i++)
    	{
    		f1=f1*i%MOD;f2=f2*qkpow(m-i+1,MOD-2)%MOD;
    		ans=(ans+b[i]*f1%MOD*f2%MOD*i%MOD*a[i])%MOD;
    	}
    	printf("%lld\n",ans);
    }
    
  • 相关阅读:
    2015第14周四
    2015第14周三
    2015第14周二
    2015第14周一
    2015第13周日
    2015第13周六
    2015第13周五
    2015第13周四
    2015第13周三
    2015第13周二
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16446842.html
Copyright © 2020-2023  润新知