[BZOJ5305] [HAOI2018] 苹果树

题目链接

BZOJ:https://lydsy.com/JudgeOnline/problem.php?id=5305

洛谷:https://www.luogu.org/problemnew/show/P4492

LOJ:https://loj.ac/problem/2526

Solution

神仙计数题...

对于一棵固定的树,对于每一条边的贡献很容易算,如果这条边较深的点为(v),那么这条边对答案的贡献就是(2sz_v(n-sz_v))

那么我们反过来考虑,我们(O(n^2))的枚举这条边的情况,即枚举深度较小的点的编号和较大的点的(size),然后统计一条这样的边在所有情况中出现了多少次。

首先我们硬点加点的时候是按编号顺序从小到达的加的,这对答案无影响。

设当前枚举到的点为(i),深度较大的点(size)(j),我们分成三部分统计。

  • (1sim i)的点没有限制,有(i!)种情况。
  • 然后剩下了(n-i)个点,我们要选出(j)个放在(i)下面,这(j)个点连的时候也没有限制,那么就是(inom{n-i}{j}j!)种情况。
  • 除开这些点剩下还有(n-i-j)个点,第一次有(i)种选法,第二次有(i+1)种...最后一次有(n-i-j+1)种选法,一共就是(prod_{k=i}^{n-i-j+1}k) 种。

那么总的贡献就是:

[ans=sum_{i=1}^{n}sum_{j=1}^{n-i}2j(n-j)cdot i!cdot j!inom{n-i}{j}cdot prod_{k=i}^{n-j-1}k ]

化简一下:

[egin{align} ans=&sum_{i=1}^{n}i!cdot 2sum_{j=1}^{n-i}j(n-j)cdot j!inom{n-i}{j}prod_{k=i}^{n-j-1}k\ =&sum_{i=1}^{n}i!cdot 2sum_{j=1}^{n-i}j(n-j)cdot frac{(n-i)!}{(n-i-j)!}prod_{k=i}^{n-j-1}k\ =&sum_{i=1}^{n}i!cdot 2sum_{j=1}^{n-i}j(n-j)cdot left(prod_{k=n-i-j+1}^{n-i}k ight)left(prod_{k=i}^{n-j-1}k ight) end{align} ]

由于没有逆元,可以预处理出(s[i][j]=prod_{k=i}^{j}k),时间复杂度(O(n^2))

#include<bits/stdc++.h>
using namespace std;

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}

#define lf double
#define ll long long 

const int maxn = 2e3+10;
const int inf = 1e9;
const lf eps = 1e-8;

int s[maxn][maxn],n,mod,ans;

int main() {
	read(n),read(mod);
	for(int i=1;i<=n;i++) {
		s[i][i]=i;
		for(int j=i+1;j<=n;j++) s[i][j]=1ll*s[i][j-1]*j%mod;
		for(int j=0;j<i;j++) s[i][j]=1;
	}
	for(int i=1;i<=n;i++) {
		int res=0;
		for(int j=1;j<=n-i;j++) res=(res+1ll*j*(n-j)%mod*s[n-i-j+1][n-i]%mod*s[i][n-j-1]%mod)%mod;
		ans=(ans+2ll*s[1][i]%mod*res%mod)%mod;
	}write(ans);
	return 0;
}
原文地址:https://www.cnblogs.com/hbyer/p/10647406.html