Codeforces 题面传送门 & 洛谷题面传送门

一种换根 dp 的做法。

首先碰到这类题目,我们很明显不能真的把图 \(G\) 建出来,因此我们需要观察一下图 \(G\) 有哪些性质。很明显我们可以把它看作 \(T_1,T_2\) 上分别有一个标记,每次可以将 \(T_1,T_2\) 上的某个标记沿着某条边移动一步,问 \(k\) 步之后标记刚好回到原位的方案数是多少。

显然两棵树是独立的,因此可以分开来考虑,我们记 \(dp1_{i,j}\) 表示 \(T_1\) 中从 \(i\) 开始走 \(j\) 步又回到 \(i\) 的方案数,\(dp2_{i,j}\) 也同理,那么显然有 \(ans=\sum\limits_{x\in T_1}\sum\limits_{y\in T_2}\sum\limits_{i=0}^kdp1_{x,i}dp2_{y,k-i}\dbinom{k}{i}\),把组合数裂开可以得到 \(ans=k!\sum\limits_{i=0}^k\sum\limits_{x\in T_1}\dfrac{dp_{x,i}}{i!}\sum\limits_{y\in T_2}\dfrac{dp_{y,k-i}}{(k-i)!}\),两部分分别求个和然后 \(\mathcal O(k)\) 合并即可。

因此接下来我们的任务就是求出 \(dp1_{i,j}\) 和 \(dp2_{i,j}\),两部分是等价的,因此考虑怎样求 \(dp1_{i,j}\),在下文中为了方便起见,我们用 \(dp_{i,j}\) 代替 \(dp1_{i,j}\),我们不妨以 \(1\) 为根做一遍 DFS,设 \(f_{x,j}\) 表示从 \(x\) 开始,只经过 \(x\) 子树内,\(j\) 步后回到 \(i\) 的方案数,那么我们显然可以枚举 \(i\) 第一步到达的点是什么,经过多少步之后第一次回到 \(x\),那么有 \(f_{x,j}=\sum\limits_{y\in\text{son}(x)}\sum\limits_{t=2k,t\le j,k\in\mathbb{Z}^+}f_{y,t-2}f_{x,j-t}\),这个显然可以一遍 DFS 带走,我们再设 \(out_{x,j}\) 表示从 \(fa_x\) 开始,不经过 \(x\) 子树内的点,\(j\) 步回到 \(x\) 的方案数,那么我们还是可以枚举第一步到达的点 \(y\) 以及第一次回到 \(fa_x\) 的时间 \(t\),那么这种情况对 \(out_{x,j}\) 的贡献就是 \(v_t\times out_{x,t}\),其中如果 \(y\) 是 \(fa_x\) 的父亲,那么 \(v_t=out_{fa_x,t}\),否则 \(v_t=f_{y,t}\),这个暴力搞是 \(\mathcal O(deg_i^2)\) 的,碰到菊花图就凉凉了,考虑优化,注意到这东西可以写成 \(\sum\limits_{t}S_t\times out_{x,t}\) 的形式,因此我们可以先提前一遍预处理求出所有子树的 \(f_{y,t}\) 之和,扫到一个子树时就把该子树内的贡献撤销掉,回溯时再加上,这样复杂度就 ok 了。最后 \(dp_{x,j}\) 的转移方程就比较容易了,还是枚举第一步到达的点 \(y\) 和第一次回到 \(x\) 的时间 \(t\),那么如果 \(y\) 是 \(x\) 的父亲贡献就是 \(out_{x,t-2}dp_{x,j-t}\),否则是 \(f_{y,t-2}dp_{x,j-t}\)。

时间复杂度 \(\mathcal O(nk^2)\)。

可以写个类封装一下,避免写过多一模一样的代码。

  1. const int MAXN=4000;
  2. const int MAXK=75;
  3. const int MOD=998244353;
  4. int k,fac[MAXK+5],ifac[MAXK+5];
  5. void init_fac(int n){
  6. for(int i=(fac[0]=ifac[0]=ifac[1]=1)+1;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
  7. for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD,ifac[i]=1ll*ifac[i-1]*ifac[i]%MOD;
  8. }
  9. struct tree_solver{
  10. int n,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0,sum[MAXK+5];
  11. void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
  12. void read(){for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);}
  13. int f[MAXN+5][MAXK+5],dp[MAXN+5][MAXK+5],out[MAXN+5][MAXK+5],dep[MAXN+5];
  14. void dfs0(int x,int fa){
  15. for(int e=hd[x];e;e=nxt[e]){
  16. int y=to[e];if(y==fa) continue;
  17. dep[y]=dep[x]+1;dfs0(y,x);
  18. }
  19. }
  20. void dfs1(int x,int fa,int t){
  21. for(int e=hd[x];e;e=nxt[e]){
  22. int y=to[e];if(y==fa) continue;dfs1(y,x,t);
  23. for(int i=0;i<t;i+=2) f[x][t]=(f[x][t]+1ll*f[x][i]*f[y][t-i-2])%MOD;
  24. }
  25. }
  26. void dfs2(int x,int fa,int t){
  27. vector<int> s(t+1);
  28. for(int e=hd[x];e;e=nxt[e]){
  29. int y=to[e];
  30. if(y==fa) for(int i=0;i<t;i+=2) s[i]=(s[i]+out[x][t-i-2])%MOD;
  31. else for(int i=0;i<t;i+=2) s[i]=(s[i]+f[y][t-i-2])%MOD;
  32. }
  33. for(int e=hd[x];e;e=nxt[e]){
  34. int y=to[e];if(y==fa) continue;
  35. for(int i=0;i<t;i+=2) s[i]=(s[i]-f[y][t-i-2]+MOD)%MOD;
  36. for(int i=0;i<t;i+=2) out[y][t]=(out[y][t]+1ll*s[i]*out[y][i])%MOD;
  37. for(int i=0;i<t;i+=2) s[i]=(s[i]+f[y][t-i-2])%MOD;
  38. dfs2(y,x,t);
  39. }
  40. }
  41. void solve(){
  42. dfs0(1,0);
  43. for(int i=1;i<=n;i++) dp[i][0]=f[i][0]=out[i][0]=1;
  44. for(int i=2;i<=k;i+=2){
  45. dfs1(1,0,i);dfs2(1,0,i);
  46. // for(int j=1;j<=n;j++) printf("%d %d %d %d\n",j,i,f[j][i],out[j][i]);
  47. }
  48. for(int i=2;i<=k;i+=2) for(int x=1;x<=n;x++){
  49. for(int e=hd[x];e;e=nxt[e]){
  50. int y=to[e];
  51. if(dep[y]>dep[x]){
  52. for(int j=0;j<i;j+=2) dp[x][i]=(dp[x][i]+1ll*dp[x][i-j-2]*f[y][j])%MOD;
  53. } else {
  54. for(int j=0;j<i;j+=2) dp[x][i]=(dp[x][i]+1ll*dp[x][i-j-2]*out[x][j])%MOD;
  55. }
  56. } //printf("%d %d %d\n",x,i,dp[x][i]);
  57. }
  58. for(int i=0;i<=k;i+=2) for(int x=1;x<=n;x++)
  59. sum[i]=(sum[i]+1ll*ifac[i]*dp[x][i])%MOD;
  60. // for(int i=0;i<=k;i+=2) printf("%d\n",sum[i]);
  61. }
  62. } t1,t2;
  63. int main(){
  64. scanf("%d%d%d",&t1.n,&t2.n,&k);if(k&1) return puts("0"),0;
  65. t1.read();t2.read();init_fac(k);t1.solve();t2.solve();
  66. int ans=0;for(int i=0;i<=k;i+=2) ans=(ans+1ll*t1.sum[i]*t2.sum[k-i])%MOD;
  67. printf("%d\n",1ll*ans*fac[k]%MOD);
  68. return 0;
  69. }

Codeforces 997D - Cycles in product(换根 dp)的更多相关文章

  1. Codeforces 891D - Sloth(换根 dp)

    Codeforces 题面传送门 & 洛谷题面传送门 换根 dp 好题. 为啥没人做/yiw 首先 \(n\) 为奇数时答案显然为 \(0\),证明显然.接下来我们着重探讨 \(n\) 是偶数 ...

  2. [BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]

    题意 给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值. \(n\leq 5\times 10^5\) . 分析 记断掉一条边之后两棵树的直径为 \ ...

  3. 2018.10.15 NOIP训练 水流成河(换根dp)

    传送门 换根dp入门题. 貌似李煜东的书上讲过? 不记得了. 先推出以1为根时的答案. 然后考虑向儿子转移. 我们记f[p]f[p]f[p]表示原树中以ppp为根的子树的答案. g[p]g[p]g[p ...

  4. 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市

    P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...

  5. 小奇的仓库:换根dp

    一道很好的换根dp题.考场上现场yy十分愉快 给定树,求每个点的到其它所有点的距离异或上m之后的值,n=100000,m<=16 只能线性复杂度求解,m又小得奇怪.或者带一个log像kx一样打一 ...

  6. 国家集训队 Crash 的文明世界(第二类斯特林数+换根dp)

    题意 ​ 题目链接:https://www.luogu.org/problem/P4827 ​ 给定一棵 \(n\) 个节点的树和一个常数 \(k\) ,对于树上的每一个节点 \(i\) ,求出 \( ...

  7. Acesrc and Travel(2019年杭电多校第八场06+HDU6662+换根dp)

    题目链接 传送门 题意 两个绝顶聪明的人在树上玩博弈,规则是轮流选择下一个要到达的点,每达到一个点时,先手和后手分别获得\(a_i,b_i\)(到达这个点时两个人都会获得)的权值,已经经过的点无法再次 ...

  8. bzoj 3566: [SHOI2014]概率充电器 数学期望+换根dp

    题意:给定一颗树,树上每个点通电概率为 $q[i]$%,每条边通电的概率为 $p[i]$%,求期望充入电的点的个数. 期望在任何时候都具有线性性,所以可以分别求每个点通电的概率(这种情况下期望=概率 ...

  9. codeforces1156D 0-1-Tree 换根dp

    题目传送门 题意: 给定一棵n个点的边权为0或1的树,一条合法的路径(x,y)(x≠y)满足,从x走到y,一旦经过边权为1的边,就不能再经过边权为0的边,求有多少边满足条件? 思路: 首先,这道题也可 ...

随机推荐

  1. Codeforces Round #747 (Div. 2) Editorial

    Codeforces Round #747 (Div. 2) A. Consecutive Sum Riddle 思路分析: 一开始想起了那个公式\(l + (l + 1) + - + (r − 1) ...

  2. Netty学习笔记(2)ByteBuffer

    1. 测试ByteBuffer 1.1 依赖 <dependencies> <dependency> <groupId>io.netty</groupId&g ...

  3. UltraSoft - Alpha - Scrum Meeting 6

    Date: Apr 21th, 2020. Scrum 情况汇报 进度情况 组员 负责 昨日进度 后两日任务 CookieLau PM 验证了课程中心获取课程资源和作业的爬虫方式 细化前后端交互中的难 ...

  4. Spring Cloud Gateway夺命连环10问?

    大家好,我是不才陈某~ 最近有很多小伙伴私信我催更 <Spring Cloud 进阶>,陈某也总结了一下,最终原因就是陈某之前力求一篇文章将一个组件重要知识点讲透,这样导致了文章篇幅很长, ...

  5. Centos 7 局域网 yum 源搭建

    一.需求及实现方式介绍: 需求:现在各个软件版本更新迭代很快,在我们部署一套集群(比如:openstack)后,如果过一段时间想扩展集群时发现软件版本早已迭代更新,安装后导致和现有环境或多或少不兼容, ...

  6. DeWeb第2个通用化模块:主控模块。 手机/电脑自适应。通过修改配置文件即可实现进入不同模块

    演示: https://delphibbs.com/main.dw 也可以通过 https://delphibbs.com/login.dw 采用admin/123456登录后自动进入 开发环境和源代 ...

  7. 微服务之十四如何在 Ocelot 网关中配置多实例 Swagger 访问

    一.介绍 当我们开发基于微服务的应用程序的时候,有一个环节总是跳不过去的,那就是要创建 WebApi,然后,我们的应用程序基于 WebApi 接口去访问.在没有 Swagger 以前,我们开发好了 W ...

  8. ES6-正则新增(复习+学习)

    ES6-正则 昨天,复习了正则的基本知识,今天学习ES6新增的正则的知识,做一个总结笔记,大家可以先看4,5对应的方法然后再从头看,话不多说直接上: 1.RegExp构造函数的区别 2.新增的修饰符 ...

  9. PTA7-1 根据后序和中序遍历输出先序遍历

    本题要求根据给定的一棵二叉树的后序遍历和中序遍历结果,输出该树的先序遍历结果. 输入格式: 第一行给出正整数N(≤30),是树中结点的个数.随后两行,每行给出N个整数,分别对应后序遍历和中序遍历结果, ...

  10. dotNET开发之MVC中Controller返回值类型ActionResult方法总结

    1.返回ViewResult视图结果,将视图呈现给网页 2. 返回PartialViewResult部分视图结果,主要用于返回部分视图内容 3. 返回ContentResult用户定义的内容类型 4. ...