2025_10_27 T3
Evan_Leo_Azir · · 题解
上位紫,NOIP T4 也完全可以胜任,疑似之前 ZR 集训遇到过,但我并未前去订正,固场上也未做出
考场数据范围为
发现,直接计算相当困难,但是注意到一点,当
n\ge2\times k
观察可得,此时,树的“长相”可以看作两颗大小为
所以,我们预处理所有可能的子树情况,再将它们拼接
如何预处理?我们先枚举一个根,做 dfs,此时如果该节点子树大小为
对于此情况,有一个显然的性质,即:树上有
注意到,假设已经确定一
所以可以确定,树的重心一定为
因此,我们以重心为根,尝试每次将一个点加入
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=4e1+5,Mod=1e9+9;
int n,k,cnte,cntn,dfn,ans,root,mins;
int head[maxn],mul[maxn],inv[maxn],f[maxn];
int siz[maxn],dep[maxn],dp[maxn][maxn>>1][maxn>>1],pre[maxn],rk[maxn];
ll nod[maxn];
struct EDGE{
int v,nxt;
}e[maxn<<1];
struct TREE{
int rot,res,siz;
ll nod;
}tree[maxn<<1];
map<ll,bool>p;
void adde(int u,int v)
{
e[cnte]={v,head[u]};
head[u]=cnte++;
}
int read()
{
int ret=0,w=1;char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+(ch^48);ch=getchar();}
return ret*w;
}
int add(int x,int y) {return x+y>=Mod?x+y-Mod:x+y;}
int qpow(int x,int y)
{
int ret=1;
while(y)
{
if(y&1) ret=1ll*ret*x%Mod;
x=1ll*x*x%Mod;
y>>=1;
}
return ret;
}
int com(int x,int y) {return 1ll*mul[y]*inv[x]%Mod*inv[y-x]%Mod;}
void pre_all()
{
mul[0]=1;
for(int i=1;i<maxn;i++) mul[i]=1ll*mul[i-1]*i%Mod;
inv[maxn-1]=qpow(mul[maxn-1],Mod-2);
for(int i=maxn-2;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%Mod;
}
void dfs1(int u,int fa)
{
nod[u]=1ll<<(u-1),f[u]=1,siz[u]=1;
for(int i=head[u],to;i!=-1;i=e[i].nxt) if((to=e[i].v)!=fa)
{
dfs1(to,u);
nod[u]|=nod[to],f[u]=1ll*f[u]*f[to]%Mod*com(siz[to],siz[u]+siz[to]-1)%Mod;
siz[u]+=siz[to];
}
if(!p[nod[u]]&&siz[u]==k)
{
p[nod[u]]=1;
tree[++cntn]={u,f[u],siz[u],nod[u]};
}
}
void dfs2(int u,int fa)
{
dep[u]=dep[fa]+1;
for(int i=head[u],to;i!=-1;i=e[i].nxt) if((to=e[i].v)!=fa) dfs2(to,u);
}
void inpu()
{
n=read(),k=read();
memset(head,-1,sizeof(head));
for(int i=1,u,v;i<n;i++)
{
u=read(),v=read();
adde(u,v);adde(v,u);
}
}
void deal() {ans=mul[n];}
void deal1()
{
for(int i=1;i<=n;i++) dfs1(i,0);
for(int i=1;i<cntn;i++)
{
dfs2(tree[i].rot,0);
for(int j=i+1;j<=cntn;j++) if(!(tree[i].nod&tree[j].nod))
{
if(dep[tree[j].rot]+tree[i].siz+tree[j].siz-2!=n) continue;
ans=add(ans,1ll*tree[i].res*tree[j].res%Mod);
}
}
ans=2ll*ans%Mod;
}
void getroot(int u,int fa)
{
int ma=0;
siz[u]=1;
for(int i=head[u],to;i!=-1;i=e[i].nxt) if((to=e[i].v)!=fa)
{
getroot(to,u);
siz[u]+=siz[to],ma=max(ma,siz[to]);
}
ma=max(ma,n-siz[u]);
if(!root||ma<mins) root=u,mins=ma;
}
void dfs_pre(int u,int fa)
{
pre[u]=siz[u]=1,rk[++dfn]=u;
for(int i=head[u],to;i!=-1;i=e[i].nxt) if((to=e[i].v)!=fa)
{
dfs_pre(to,u);
pre[u]=1ll*pre[u]*pre[to]%Mod*com(siz[to],siz[u]+siz[to]-1)%Mod;
siz[u]+=siz[to];
}
}
void deal2()
{
getroot(1,0);
dfs_pre(root,0);
dp[n+1][0][0]=1;
for(int i=n;i>=1;i--) for(int j=0;j<=n-k;j++) for(int p=0;p<=n-k;p++)
{
int u=rk[i];
if(n-i+1>=j+p) dp[i][j][p]=add(dp[i][j][p],1ll*dp[i+1][j][p]*((n-i+1)-j-p)%Mod);
if(j+siz[u]<=n-k) dp[i][j+siz[u]][p]=add(dp[i][j+siz[u]][p],1ll*dp[i+siz[u]][j][p]*pre[u]%Mod*com(siz[u],siz[u]+j)%Mod);
if(p+siz[u]<=n-k) dp[i][j][p+siz[u]]=add(dp[i][j][p+siz[u]],1ll*dp[i+siz[u]][j][p]*pre[u]%Mod*com(siz[u],siz[u]+p)%Mod);
}
ans=dp[1][n-k][n-k];
}
void solve()
{
inpu();
if(k==1) deal();
else if((k<<1)<=n) deal1();
else deal2();
printf("%d",ans);
}
int main()
{
pre_all();
int t=1;
while(t--) solve();
return 0;
}
2025_11_5 注:由于考场数据为