CF997D
分析:
假设在第一个树上我们有一个长度为x的环,在第二树上我们有一个长度为y的环,那么可以在叉积树上构造出$\binom{x+y}{x}$个长度为x+y的环
问题的关键就变成了如何统计出在一个树上的长度为i的环的个数
设$f(u,v,k)$表示从u点出发走k步回到u点,中途不经过点v的方案数,其中v是u的相邻点
考虑求解的转移过程,一定是从u走到某个邻接点w(w!=v),然后从w走到w(不经过u),然后再回到u,于是有转移方程
这个是$O(n^2k^2)$的,但明显里面的w不需要枚举,只需要拿sum减去w=v的情况就行了,于是变成了$O(nk^2)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mp make_pair 4 const int maxn=4000,mod=998244353; 5 int k; 6 int ans; 7 int C[80][80]; 8 void inc(int &a,int b) 9 { 10 a=(a+b)%mod; 11 } 12 struct wjmzbmr 13 { 14 int n; 15 vector<int> g[maxn+5]; 16 vector<int> dp[80][maxn+5]; 17 int sum[80][maxn+5]; 18 int ans[maxn+5],sz[maxn+5]; 19 map<pair<int,int>,int> s; 20 void init() 21 { 22 for(int i=1;i<n;++i) 23 { 24 int u,v; 25 scanf("%d%d",&u,&v); 26 g[u].push_back(v),g[v].push_back(u); 27 } 28 for(int i=1;i<=n;++i) 29 for(int j=0;j<g[i].size();++j) 30 s[mp(i,g[i][j])]=j; 31 for(int i=1;i<=n;++i) sz[i]=g[i].size(),g[i].push_back(0); 32 for(int t=0;t<=k;++t) 33 for(int i=0;i<=n;++i) 34 dp[t][i].resize(sz[i]+1,0); 35 } 36 void work() 37 { 38 for(int i=1;i<=n;++i) 39 for(int j=0;j<=sz[i];++j) 40 { 41 42 dp[0][i][j]=1; 43 inc(sum[0][g[i][j]],1); 44 } 45 for(int i=2;i<=k;++i) 46 for(int u=1;u<=n;++u) 47 for(int j=0;j<=sz[u];++j) 48 { 49 int v; 50 if(j<sz[u]) v=g[u][j];else v=0; 51 int id; 52 if(v==0) id=0; 53 else 54 id=s[mp(v,u)]; 55 for(int t=0;t<=i-2;++t) 56 dp[i][u][j]=((dp[i][u][j]+1LL*dp[i-t-2][u][j]*(sum[t][u]-dp[t][v][id])%mod)%mod+mod)%mod; 57 inc(sum[i][v],dp[i][u][j]); 58 } 59 for(int i=0;i<=k;i+=2) 60 for(int u=1;u<=n;++u) 61 inc(ans[i],dp[i][u][sz[u]]); 62 } 63 }tree[2]; 64 int main() 65 { 66 //freopen("ce.in","r",stdin); 67 scanf("%d%d%d",&tree[0].n,&tree[1].n,&k); 68 tree[0].init(),tree[1].init(); 69 tree[0].work(); 70 tree[1].work(); 71 C[0][0]=1; 72 for(int i=1;i<=k;++i) 73 { 74 C[i][0]=1; 75 for(int j=1;j<=i;++j) 76 C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; 77 } 78 for(int i=0;i<=k;++i) 79 inc(ans,int(1LL*tree[0].ans[i]*tree[1].ans[k-i]%mod*C[k][i]%mod)); 80 printf("%d\n",ans); 81 return 0; 82 }