题目链接
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\),我们分成三部分统计。
- \(1\sim i\)的点没有限制,有\(i!\)种情况。
- 然后剩下了\(n-i\)个点,我们要选出\(j\)个放在\(i\)下面,这\(j\)个点连的时候也没有限制,那么就是\(\binom{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!\binom{n-i}{j}\cdot \prod_{k=i}^{n-j-1}k \]
化简一下:
\[ \begin{align} ans=&\sum_{i=1}^{n}i!\cdot 2\sum_{j=1}^{n-i}j(n-j)\cdot j!\binom{n-i}{j}\prod_{k=i}^{n-j-1}k\\ =&\sum_{i=1}^{n}i!\cdot 2\sum_{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 2\sum_{j=1}^{n-i}j(n-j)\cdot \left(\prod_{k=n-i-j+1}^{n-i}k\right)\left(\prod_{k=i}^{n-j-1}k\right) \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('\n');}
#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;
}