題目
正解
參考:
官方題解:https://blog.csdn.net/qq_16267919/article/details/79675232
https://www.luogu.com.cn/blog/ShadowassIIXVIIIIV/solution-p4337
(極度推薦這篇博客,講解得非常詳細)
由於上面的那篇博客講得比較清楚,所以我這裏就簡單地概括一下:
首先考慮中的每個點代表什麼:
中一個點代表中一條邊。
中一個點代表中相連的兩條邊。
中一個點代表中相連的三條邊(長度爲的鏈(包括三元環)、或一個點連出的三條邊)。
於是總結出中的一個點表示中的條邊組成的連通塊。
這樣表述有些問題,修正一下:中的一個點表示中的不超過條邊組成的連通塊。
並且相同的連通塊可能被多個節點表示。
然後又可以發現,對於中的一個連通塊,求,它恰好是的一個連通塊。
想要比較好的理解這些性質,建議拿幾個樣例來手玩一下。上面推薦的那篇博客舉的樣例不錯,手玩一下就能夠比較好理解。
由於題目中的是一棵樹,所以這也可以轉化成不超過個點組成的連通塊。
枚舉一個不超過個點的連通塊,計算中有多少個表示的點,記爲。然後在中找不同的的個數,記爲。最後就是答案。
爲了方便這裏指有根樹。
計算:
考慮將的點數算出來,作爲。這時候發現會算多,因爲這把的聯通子圖的貢獻都算了進去。
於是枚舉的聯通子圖,計算,減去即可。
枚舉聯通子圖的時間相比於下面是比較少的,忽略不計。
如果,可以通過人類智慧將的點數求出來(此時不需要保證是棵樹):
時,答案爲邊數。
時,答案爲中有多少對相鄰的邊,於是答案爲
時,答案爲中有多少條長度爲的鏈和一個點連出去三條邊的方案數(注意這個每個方案貢獻爲)。
時,考慮,通過將中每個點(對應中一條邊)的度數算出來,套進的式子中,化一下式子就出來了。
時間複雜度都是。
具體式子上面推薦的博客有。
更大咋辦?暴力算出,然後套用上面的方法算出。
考慮迭代一次點數大概乘,所以時間複雜度大概爲,點數大概開到,邊數我開到了(可能可以少些吧)。
於是這一部分爲,其中爲大小小於等於的本質不同的有根樹個數。
有個大優化:做無根樹哈希,如果當前的答案之前算過就不用計算。
計算:
設表示將有根樹的根放到節點上,多少種方案。
轉移的時候枚舉有根樹的根的每個兒子所代表的子樹,和的兒子匹配。套一個狀壓DP實現。
這樣時間複雜度是
似乎有點慢,加個小優化:不考慮有根樹的根的每個兒子直接是葉子節點的情況。狀壓DP之後再將葉子結點用個組合數計算貢獻。
注意在算的過程中可能會有重複計算的情況,於是對於有根樹的根的兒子中,對於每種不同的子樹計算相同的個數,答案除以它們的階乘。(其實也可以在狀壓的時候不用二進制壓表示每個子樹選或不選,而是壓每種子樹用了多少個。這樣理論上還快些。)
時間複雜度。
代碼
8k,我醉了……
講一下實現細節(不一定和程序中一樣):
處理不同的有根樹,而且還要處理出根的兒子子樹的編號。
我程序中的方法從有根樹點數小到大枚舉,枚舉括號序。枚舉之後判斷兒子子樹的編號是否有序,如果不有序就不算。
應該還有一種比較優美的方式:按點數從小到大枚舉。枚舉直接與根相連的子樹的種類,枚舉的過程保證編號不上升。然後給枚舉出來的子樹標號。
無根樹哈希大概就是找重心。如果重心有兩個就在邊中間插一個點。
以其爲根求括號序。
由於連出的兒子本是無序的,所以先給連出的兒子的哈希值排序之後再計算。
枚舉一棵樹的聯通子圖的時候,先算不包括根的聯通子圖。設表示中所有聯通子圖的之和,之前已經處理了,直接算。
然後算包括根的聯通子圖。先求序,對於一個點,如果它選,下一個考慮就是它的第一個兒子,否則下一個考慮它子樹之外。
最後提醒:模塊化!模塊化!
(話說那些3k或4k的怎麼做到的?)
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <cassert>
#define ll long long
const int N=5010;
const int mo=998244353;
const int inv2=499122177;
int n,k;
//Section:fac,ifac,C(m,n)
ll fac[N],ifac[N];
void initC(int n){
ifac[1]=1;
for (int i=2;i<=n;++i)
ifac[i]=(mo-mo/i)*ifac[mo%i]%mo;
fac[0]=ifac[0]=1;
for (int i=1;i<=n;++i){
fac[i]=fac[i-1]*i%mo;
ifac[i]=ifac[i-1]*ifac[i]%mo;
}
}
ll C(int m,int n){return fac[m]*ifac[n]%mo*ifac[m-n]%mo;}
//Section:Graph
struct EDGE{int to;EDGE *las;};
template <int _N,int _M>
struct Graph{
int n=_N;
EDGE e[_M];
int ne;
EDGE *last[_N+1];
void init(int _n=0){n=_n,ne=0,memset(last,0,sizeof(EDGE*)*(n+1));}
void link(int u,int v){e[ne]={v,last[u]};last[u]=e+ne++;}
};
Graph<N,N*2> G;
//Section:Get Rooted Tree
map<int,int> id;
int cnt;
int siz[1300];
vector<int> son[1300];
int lf[1300],same[1300];
//lf:Num of leaves connecting to rt directly
//same:Pro of 1/(Num of same subT connecting to rt directly)!
int rt_hash(int n,int s){return s*22+2*n-1;}
void grt(int x,int k,int sum,int s){
if (sum<0)
return;
if (x==2*k-2){
if (sum)
return;
s=(s<<1)+(1<<2*k-1);
++cnt;
int p=0,_lf=0,_same=1,lst=0,c=0;
for (int i=1,j=1;i<2*k-1;++i){
p+=(s>>i&1?-1:1);
if (p==0){
int a=rt_hash(i-j+1>>1,(s&(1<<i+1)-1)>>j);
if (id.find(a)==id.end()){son[cnt--].clear();return;}
a=id[a];
son[cnt].push_back(a);
_lf+=(a==1);
if (a==lst)
c++;
else{
_same=(ll)_same*ifac[c]%mo;
lst=a,c=1;
}
j=i+1;
}
}
_same=(ll)_same*ifac[c]%mo;
for (int i=1;i<son[cnt].size();++i)
if (son[cnt][i-1]>son[cnt][i]){son[cnt--].clear();return;}
int key=rt_hash(k,s);
id[key]=cnt;
siz[cnt]=k,lf[cnt]=_lf,same[cnt]=_same;
return;
}
grt(x+1,k,sum+1,s);
grt(x+1,k,sum-1,s+(1<<x));
}
//Sectioon: Hash:Unrooted Tree
template <int _N,int _M>
void build_ut(int x,int t,int &n,Graph<_N,_M> &G){//build UT by id of RT
if (siz[t]==1)
return;
for (int i=0;i<son[t].size();++i){
++n,G.link(x,n),G.link(n,x);
build_ut(n,son[t][i],n,G);
}
}
int G0,G1,all;
template <int _N,int _M>
void findG(int x,int fa,Graph<_N,_M> &G,int siz[]){//find center of gravity
siz[x]=1;
bool is=1;
for (EDGE *ei=G.last[x];ei;ei=ei->las)
if (ei->to!=fa){
findG(ei->to,x,G,siz);
siz[x]+=siz[ei->to];
is&=(siz[ei->to]<=all>>1);
}
is&=(all-siz[x]<=all>>1);
if (is) (G0?G1:G0)=x;
}
int *_siz,*_key;
bool cmpp(int a,int b){return _siz[a]<_siz[b] || _siz[a]==_siz[b] && _key[a]<_key[b];}
template <int _N,int _M>
void gethash(int x,int fa,Graph<_N,_M> &G,int siz[],int key[]){
siz[x]=1;
for (EDGE *ei=G.last[x];ei;ei=ei->las)
if (ei->to!=fa)
gethash(ei->to,x,G,siz,key),siz[x]+=siz[ei->to];
static int p[12];
int cnt=0;
for (EDGE *ei=G.last[x];ei;ei=ei->las)
if (ei->to!=fa)
p[cnt++]=ei->to;
_siz=siz,_key=key;
sort(p,p+cnt,cmpp);
key[x]=0;
for (int i=cnt-1,s=0;i>=0;--i){
key[x]+=key[p[i]]<<s;
s+=siz[p[i]]*2;
}
key[x]=(key[x]<<1)+1;
}
template<int _N,int _M>
int ut_hash(Graph<_N,_M> &G,int n,bool rem=1){//rem:whether G can be changed
static int sz[12],key[12];
G0=G1=0,all=n,findG(1,0,G,sz);
int rt=G0;
if (G1){
++n,G.last[n]=NULL;
G.link(n,G0),G.link(n,G1);
for (EDGE *ei=G.last[G0];ei;ei=ei->las)
if (ei->to==G1){ei->to=n;break;}
for (EDGE *ei=G.last[G1];ei;ei=ei->las)
if (ei->to==G0){ei->to=n;break;}
rt=n;
}
gethash(rt,0,G,sz,key);
ll res=rt_hash(key[rt],n)*(G1?-1:1);
if (!rem && G1){
G.ne-=2;
for (EDGE *ei=G.last[G0];ei;ei=ei->las)
if (ei->to==n){ei->to=G1;break;}
for (EDGE *ei=G.last[G1];ei;ei=ei->las)
if (ei->to==n){ei->to=G0;break;}
G.last[n--]=NULL;
}
return res;
}
int rt_to_ut(int t){
static Graph<11,11*2> G;
G.init(siz[t]+1);
int n;
build_ut(1,t,n=1,G);
return ut_hash(G,siz[t]);
}
//Section: Calc w
int w[N],sw[N];
map<int,int> ut_w,ut_sw;
Graph<100010,10000010> L[2];
Graph<11,11*2> T,S;
template <int _N,int _M>
int calc234(Graph<_N,_M> &G,int k){
static int deg[100010];
if (k==0)
return G.n;
if (k==1)
return G.ne/2;
memset(deg,0,sizeof(int)*(G.n+1));
for (int i=1;i<=G.n;++i)
for (EDGE *ei=G.last[i];ei;ei=ei->las)
if (i<ei->to)
deg[i]++,deg[ei->to]++;
ll r=0;
if (k==2){
for (int i=1;i<=G.n;++i)
(r+=(ll)deg[i]*(deg[i]-1)%mo*inv2)%=mo;
}
if (k==3){
for (int i=1;i<=G.n;++i)
for (EDGE *ei=G.last[i];ei;ei=ei->las)
if (i<ei->to)
(r+=(ll)(deg[i]-1)*(deg[ei->to]-1))%=mo;
for (int i=1;i<=G.n;++i)
(r+=(ll)deg[i]*(deg[i]-1)%mo*(deg[i]-2)%mo*inv2)%=mo;
}
if (k==4){
for (int i=1;i<=G.n;++i){
ll d2=0;
for (EDGE *ei=G.last[i];ei;ei=ei->las){
ll d1=deg[i]+deg[ei->to]-2;
d2+=d1-1;
}
(r+=d2*d2)%=mo;
}
r=r*inv2%mo;
for (int i=1;i<=G.n;++i)
for (EDGE *ei=G.last[i];ei;ei=ei->las)
if (i<ei->to){
ll d1=deg[i]+deg[ei->to]-2;
r=((r-(d1-1)*(d1-1))%mo+mo)%mo;
}
for (int i=1;i<=G.n;++i)
for (EDGE *ei=G.last[i];ei;ei=ei->las)
if (i<ei->to){
ll d1=deg[i]+deg[ei->to]-2;
(r+=d1*(d1-1)%mo*(d1-2)%mo*inv2)%=mo;
}
}
return r;
}
template <int _N,int _M>
void trans(Graph<_N,_M> &G,Graph<_N,_M> &F){
F.init(G.ne/2);
for (int i=1;i<=G.n;++i)
for (EDGE *ei=G.last[i];ei;ei=ei->las){
int u=(ei-G.e>>1)+1;
for (EDGE *ej=ei->las;ej;ej=ej->las){
int v=(ej-G.e>>1)+1;
F.link(u,v),F.link(v,u);
}
}
}
int fa[12],in[12],out[12],nowdfn,re[12],num[12];
template<int _N,int _M>
void getdfn(int x,Graph<_N,_M> &G){
in[x]=++nowdfn;
re[nowdfn]=x;
for (EDGE *ei=G.last[x];ei;ei=ei->las)
if (ei->to!=fa[x])
fa[ei->to]=x,getdfn(ei->to,G);
out[x]=nowdfn;
}
template<int _N,int _M>
void find_st(int k,int n,Graph<_N,_M> &G,Graph<_N,_M> &S,int &res){
int x=re[k];
if (k>G.n){
if (n<G.n)
(res+=ut_w[ut_hash(S,n,0)])%=mo;
return;
}
find_st(out[x]+1,n,G,S,res);
num[x]=++n;
S.link(num[fa[x]],num[x]);
S.link(num[x],num[fa[x]]);
find_st(k+1,n,G,S,res);
S.ne-=2;
S.last[num[fa[x]]]=S.last[num[fa[x]]]->las;
S.last[num[x]]=S.last[num[x]]->las;
}
int calcw(int t){
int key=rt_to_ut(t);
if (ut_w.find(key)!=ut_w.end()){
sw[t]=ut_sw[key];
return ut_w[key];
}
L[0].init(siz[t]);
int tot;
build_ut(1,t,tot=1,L[0]);
int now=0,las=1;
for (int i=1;i<=k-4;++i){
swap(now,las);
trans(L[las],L[now]);
}
ll res=calc234(L[now],4);
for (int i=0;i<son[t].size();++i)
(sw[t]+=sw[son[t][i]])%=mo;
T.init(siz[t]);
build_ut(1,t,tot=1,T);
nowdfn=0,fa[1]=0,getdfn(1,T);
S.init(siz[t]);
num[1]=1,find_st(2,1,T,S,sw[t]);
res=(res-sw[t]+mo)%mo;
ut_sw[key]=(sw[t]+=res)%=mo;
return ut_w[key]=res;
}
//Section:Calc t
int t[N];
int f[N][1300];
void dp(int x,int fa){
int d=0;
for (EDGE *ei=G.last[x];ei;ei=ei->las)
if (ei->to!=fa)
dp(ei->to,x),++d;
static ll g[1024];
for (int j=1;j<=cnt;++j){
if (son[j].size()>d){f[x][j]=0;continue;}
int p=son[j].size()-lf[j];
memset(g,0,sizeof(ll)*(1<<p));
g[0]=1;
for (EDGE *ei=G.last[x];ei;ei=ei->las)
if (ei->to!=fa){
int y=ei->to;
for (int i=(1<<p)-1;i>=0;--i)
for (int k=0;k<p;++k)
if (!(i>>k&1)){
int id=son[j][lf[j]+k];
(g[i|1<<k]+=(ll)g[i]*f[y][id])%=mo;
}
}
f[x][j]=(ll)g[(1<<p)-1]*C(d-p,lf[j])%mo*fac[lf[j]]%mo*same[j]%mo;
(t[j]+=f[x][j])%=mo;
}
}
int main(){
scanf("%d%d",&n,&k);
initC(max(n,k+1));
G.init(n);
for (int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
G.link(u,v);
G.link(v,u);
}
if (k<=4){
printf("%d\n",calc234(G,k));
return 0;
}
for (int i=1;i<=k+1;++i)
grt(0,i,0,0);
for (int i=1;i<=cnt;++i)
w[i]=calcw(i);
dp(1,0);
ll ans=0;
for (int i=1;i<=cnt;++i)
(ans+=(ll)w[i]*t[i])%=mo;
printf("%lld\n",ans);
return 0;
}