重心为根时每条边都可以取到上界

phil071128

2024-11-20 21:00:32

Solution

NOIp 模拟赛 T3,AT 3200 分。还是很可做的!

考虑一条边 (u,v) 对答案贡献的上界:即 2\min(siz_u,siz_v),如果固定根的话就是 2\min(siz_u,n-siz_u)。这实际上是很多问题的经典思路。

每一条边尝试能否取到上界,实际上是和重心相关的经典思路。先把重心拉成根,那么需要每个 y\in T(x) 都是往子树外连的,即 p_y\not\in T(x)。容易发现都可以取到上界。

进一步,发现我们只需对重心的儿子(称为二级根)考虑。那么问题转换成了:有多少 \{p_i\},使得 (i,p_i) 不在二级根的同一子树。

考虑容斥,限制为:i,p_i 在根的同一子树。设 g_{u,k} 为二级根 u 的子树内钦定有 k 个限制,f_{rt,k} 表示重心内钦定有 k 个限制。最终答案要恰好 0 个限制,那么二项式反演即可求出答案。

ans=\sum (-1)^k f_{rt,k} (n-k)! 而重心 $rt$ 的答案只需要做一次树形背包合并即可。 时空复杂度均为 $O(n^2)$。 ```cpp #include <bits/stdc++.h> #define int long long #define pii pair<int,int> #define fi first #define se second using namespace std; int read(){ char c=getchar();int h=0,tag=1; while(!isdigit(c)) tag=(c=='-'?-1:1),c=getchar(); while(isdigit(c)) h=(h<<1)+(h<<3)+(c^48),c=getchar(); return h*tag; } void fil(){ freopen("data.in","r",stdin); freopen("data.out","w",stdout); } int rt,n; const int N=5010; int siz[N];vector<int>s[N]; void findrt(int x,int fa) { siz[x]=1; int maxson=-1; for(int y:s[x]) { if(y==fa) continue; findrt(y,x); siz[x]+=siz[y]; maxson=max(maxson,siz[y]); } int tmp=max(n-siz[x],maxson); if(tmp<=n/2) rt=x; } void calcsiz(int x,int fa) { siz[x]=1; for(int y:s[x]) { if(y==fa) continue; calcsiz(y,x);siz[x]+=siz[y]; } } int fac[N],inv[N]; const int mod=1e9+7; int ksm(int a,int b) { if(b==1) return a; int s=ksm(a,b/2); s=s*s%mod; if(b%2==1) s=s*a%mod; return s; } int binom(int n,int m) { return fac[n]*inv[m]%mod*inv[n-m]%mod; } int g[N][N],tmp[N],dp[N]; int sgn(int x) { if(x%2==1) return mod-1; else return 1; } signed main(){ //fil(); n=read(); fac[0]=inv[0]=1; for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod,inv[i]=ksm(fac[i],mod-2)%mod; for(int i=1;i<n;i++) { int u=read(),v=read(); s[u].push_back(v);s[v].push_back(u); } findrt(1,0); calcsiz(rt,0); vector<int>vec; for(int y:s[rt]) { vec.push_back(y); } for(int i=0;i<vec.size();i++) { int v=vec[i]; for(int k=0;k<=siz[v];k++) g[v][k]=binom(siz[v],k)*binom(siz[v],k)%mod*fac[k]%mod; } int up=1; dp[0]=1; for(int i=0;i<vec.size();i++) { int v=vec[i];int nup=up+siz[v]; for(int j=0;j<=up;j++) { for(int k=0;k<=siz[v]&&j+k<=nup;k++) { tmp[j+k]=(tmp[j+k]+g[v][k]*dp[j]%mod)%mod; } } for(int j=0;j<=nup;j++) dp[j]=tmp[j],tmp[j]=0; up=nup; } int ans=0; for(int i=0;i<=n;i++) ans=(ans+sgn(i)*dp[i]%mod*fac[n-i]%mod)%mod; cout<<ans<<'\n'; return 0; } ```