P4582 [FJOI2014] 树的重心
题意
给一颗 \(n\) 个点树,问祂有多少个生成子图,重心和原树完全一致。
\(n\le 200\)。
思路
先把原树的重心求出来,然后分类讨论。
\(Part1\):一个重心
把重心当作根,跑树形 \(DP\)。设状态 \(f_{i,j}\) 表示以 \(i\) 为根的子树种选了 \(j\) 个点的方案数(\(i\) 点也选了),转移直接跑树形背包。
然后枚举 \(x\) 表示删掉 \(x\) 个点,那么每个子树的大小就不能超过 \(\lfloor\frac{n-x-1}2\rfloor\),在跑一边背包即可。
\(Part2\):两个重心
这两个重心是连着的,考虑将祂们之间的边切断,然后分别作为根,跑树形 \(DP\)。设状态 \(f_{i,j}\) 表示以 \(i\) 为根的子树种选了 \(j\) 个点的方案数(\(i\) 点也选了),转移直接跑树形背包。为了使重心依旧是那两个点,砍完边后的两棵树上选的点数应是一样的,这样统计答案就很简单了。
代码
/*
Luogu P4582 [FJOI2014] 树的重心
2026-04-13
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
template<typename T>
inline void read(T&x){
x=0;char c=getchar();bool f=0;
while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
f?x=-x:0;
}
template<typename T>
inline void write(T x){
if(x==0){putchar('0');return ;}
x<0?x=-x,putchar('-'):0;short st[50],top=0;
while(x) st[++top]=x%10,x/=10;
while(top) putchar(st[top--]+'0');
}
inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
inline void write(char c){putchar(c);}
inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
template<int mod>struct Modint{
int z;
Modint(){z=0;}
Modint(int x){x%=mod;z=x<0?x+mod:x;}
Modint(long long x){x%=mod;z=x<0?x+mod:x;}
Modint(short x){x%=mod;z=x<0?x+mod:x;}
Modint(char x){x%=mod;z=x<0?x+mod:x;}
Modint(bool x){x%=mod;z=x<0?x+mod:x;}
friend Modint operator+(Modint t,Modint t2){Modint ans;ans.z=(t.z+t2.z)%mod;return ans;}
friend Modint operator*(Modint t,Modint t2){Modint ans;ans.z=1ll*t.z*t2.z%mod;return ans;}
friend Modint operator-(Modint t,Modint t2){Modint ans;ans.z=(t.z-t2.z)%mod;return ans;}
Modint operator<<(const int t)const{Modint ans;ans.z=(z<<t)%mod;return ans;}
Modint operator>>(const int t)const{Modint ans;ans.z=(z>>t)%mod;return ans;}
Modint&operator+=(const Modint t){z=(z+t.z)%mod;return *this;}
Modint&operator*=(const Modint t){z=1ll*z*t.z%mod;return *this;}
Modint&operator-=(const Modint t){z=(z-t.z)%mod;return *this;}
Modint&operator<<=(const int t){z=(z<<t)%mod;return *this;}
Modint&operator>>=(const int t){z=(z>>t)%mod;return *this;}
Modint&operator++(){z++,z%=mod;return *this;}
Modint&operator--(){z--,z%=mod;return *this;}
Modint operator++(int){Modint ls=*this;z++,z%=mod;return ls;}
Modint operator--(int){Modint ls=*this;z--,z%=mod;return ls;}
friend Modint ksm(Modint a,int b){
Modint ans=1;
while(b){if(b&1) ans=ans*a;a=a*a,b>>=1;}
return ans;
}
friend void read(Modint&z){
int x=0;char c=getchar();bool f=0;
while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
while(isdigit(c)) x=(x*10ll+c-'0')%mod,c=getchar();
f?x=-x:0;
z.z=x;
}
friend void write(Modint x){x.z<0?x.z+=mod:0;write(x.z);}
};
const int mod=10007,maxn=210;
#define M Modint<mod>
int n,son[maxn],sz[maxn];
vector<int>e[maxn];
vector<int>rt;
namespace Part1{
M f[maxn][maxn],ff[maxn];//f[i][j]:i的子树内选j个点的方案数
void dp(int u,int fa){
f[u][1]=1,sz[u]=1;
for(int v:e[u]){
if(v==fa) continue;
dp(v,u);
for(int i=sz[u];i>=1;i--) for(int j=sz[v];j>=1;j--) f[u][i+j]+=f[u][i]*f[v][j];
sz[u]+=sz[v];
}
}
void solve(int rt){
memset(f,0,sizeof(f)),memset(ff,0,sizeof(ff));
for(int v:e[rt]) dp(v,rt);
int nw=1;
M ans=0;
for(int x=0;x<=n;x++){
int maxx=(n-x-1)/2;
nw=1,ff[1]=1;
for(int v:e[rt]){
for(int i=nw;i>=1;i--) for(int j=min(sz[v],maxx);j>=1;j--) ff[i+j]+=ff[i]*f[v][j];
nw+=sz[v];
}
ans+=ff[n-x];
for(int i=1;i<=n;i++) ff[i]=0;
}
write(ans);write("\n");
}
};
namespace Part2{
M f[maxn][maxn];
void dp(int u,int fa){
f[u][1]=1,sz[u]=1;
for(int v:e[u]){
if(v==fa) continue;
dp(v,u);
for(int i=sz[u];i>=1;i--) for(int j=sz[v];j>=1;j--) f[u][i+j]+=f[u][i]*f[v][j];
sz[u]+=sz[v];
}
}
void solve(int rt1,int rt2){
memset(f,0,sizeof(f));
dp(rt1,rt2),dp(rt2,rt1);
M ans;
for(int i=1;i<=n;i++) ans+=f[rt1][i]*f[rt2][i];
write(ans);write("\n");
}
}
void get_rt(int u,int fa=0){
sz[u]=1;son[u]=0;
for(int v:e[u]){
if(v==fa) continue;
get_rt(v,u);
sz[u]+=sz[v];
son[u]=max(son[u],sz[v]);
}
son[u]=max(son[u],n-sz[u]);
if(son[u]<son[rt[0]]) rt.clear(),rt.push_back(u);
else if(son[u]==son[rt[0]]) rt.push_back(u);
}
void solve(){
for(int i=1;i<=n;i++) e[i].clear();
read(n);
for(int i=1;i<n;i++){
int u,v;read(u,v);
e[u].push_back(v);
e[v].push_back(u);
}
son[0]=n;
rt.clear();
rt.push_back(0);
get_rt(1);
if(rt.size()==1) Part1::solve(rt[0]);
else Part2::solve(rt[0],rt[1]);
}
signed main(){
int T;read(T);
for(int t=1;t<=T;t++) printf("Case %d: ",t),solve();
return 0;
}

浙公网安备 33010602011771号