我尽力写一篇比较详细的题解。。。。

LOJ 6240. 仙人掌

我先来给你安利一个题 [BZOJ3451]Tyvj1953 Normal (DSU/点分治+NTT/FFT)

同样的,我们计算每一个点对对于答案的贡献

借一下别人严谨的分析

我们分析这个所谓可以\(O(n^3)\)实现的dp

(下文提到路径是指经过路径上的树边和环边)

定义\(dp[i][j]\)当前根时,\(i\)到根节点的路径上所有经过的点中有\(j\)个点在根节点前面被选的合法方案数

记根到\(i\)的路径上经过的点个数为\(sz[i]\)

考虑特殊情况,如果是一棵树,那么\(j\)一定为0

考虑最后的答案,就是这些点排列的总方案中合法的部分,即\(P_{root,i}=\sum dp[i][j]\cdot \cfrac{j!(sz[i]-1-j)!}{sz[i]!}\)

(\(sz[i]!\):这些点顺序总方案,\(j!\)在根前面选的点可以排列,\((sz[i]-1-j)!\)在根节点后面选的点可以排列)

我们把树边看做一个二元环,就只用考虑环上的关系了

合法的情况,就是环上点\(x,y\)两边夹的两段环(设长度为\(len1,len2\)),之中有一段上的一些点(或者就没有)被选

我们可以枚举\(len1,len2\),乘上组合数向\(dp[v][i+j]\)转移

  1. rep(i,0,sz[u]) if(dp[u][i]) {
  2. rep(j,0,len1) dp[v][i+j]=(dp[v][i+j]+dp[u][i]*C[len1][j])%P;
  3. rep(j,1,len2) dp[v][i+j]=(dp[v][i+j]+dp[u][i]*C[len2][j])%P;
  4. }

注意第二个循环不能到0,会算重复

然后我们会喜提\(O(n^4)\)写法

那么如何优化呢?

(转移显然可以NTT优化!)

其实我们枚举\(len1,len2\)的本质是从每一个可选点的集合里选出一部分点,最后在将它们累加起来

事实上,并没有必要直接枚举每一个集合里选的点

我们可以先算我们从哪些合法点集(就是上文中的\(len1,len2\))里选点,最后考虑从合法点集的集合中选点

也就是说\(\sum..\sum C(a_i,j_k)\)=\(C(\sum a_i,j)\) (额,意思是从若干个大小为\(a_i\)的集合中的每一个任意选一些等价于从所有的\(\sum a_i\)中选一部分)

令\(f[i][j]\)表示从合法集合中选一部分,最终得到的合法点集的集合中总点数为\(j\)的方案数

那么我们可以得到一个简单的转化:\(f[i][j] \rightarrow dp[i][k] \cdot C(j,k)\)

考虑\(f[i][j]\)的转移

每次我们有两个合法集合\(len1,len2\),这两个集合我们只能取一个,所以有\(f[u][i]\rightarrow f[v][i+len1],f[v][i+len2]\)

但是还有一个问题,我们现在考虑这两个集合,会有两个集合都不选的情况,这会被算两遍

所以我们要多一个类似容斥?

\(-1 \cdot f[u][i] \rightarrow f[v][i]\)(这个-1是为了突出!)

我们完成了\(n^3\)转移,但是统计答案依然要枚举\(sz[u]^2\)的时间

  1. rep(i,0,sz[u]) if(dp[u][i]) {
  2. dp[v][i+len1]=(dp[v][i+len1]+dp[u][i])%P;
  3. dp[v][i+len2]=(dp[v][i+len2]+dp[u][i])%P;
  4. dp[v][i]=(dp[v][i]-dp[u][i])%P;
  5. }

其实要不要\(dp[u][i]\)的转移其实是一样的,我们转化一下,直接跳过

直接考虑\(Ans=\sum _0^if[u][i]\cdot C(i,j)\cdot \cfrac{j!(sz[i]-1-j)!}{sz[i]!}\)

右边那一坨常数对于每一个\((sz[i],i)\)的元组都是一样的,可以预处理出来

得到\(O(n^3)\)解法

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define pb push_back
  4. #define reg register
  5. typedef long long ll;
  6. #define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
  7. #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
  8. #define Mod2(x) ((x<0)&&(x+=P))
  9. #define Mod1(x) ((x>=P)&&(x-=P))
  10. template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
  11. template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
  12. char IO;
  13. template <class T=int> T rd(){
  14. T s=0;
  15. int f=0;
  16. while(!isdigit(IO=getchar())) f|=(IO=='-');
  17. do s=(s<<1)+(s<<3)+(IO^'0');
  18. while(isdigit(IO=getchar()));
  19. return f?-s:s;
  20. }
  21. const int N=810,P=998244353;
  22. int n,m;
  23. ll dp[N][N],Inv[N],Fac[N],C[N][N],ans;
  24. vector <int> G[N],E[N];
  25. int t[N],low[N],dfn,cirsz[N],cirid[N],tmp[N],cnt,sz[N];
  26. int stk[N],top;
  27. void dfs1(int u,int f) { // 对于当前根处理仙人掌
  28. low[u]=t[u]=++dfn;
  29. stk[++top]=u;
  30. for(int v:G[u]) if(v!=f) {
  31. if(!t[v]) {
  32. dfs1(v,u);
  33. cmin(low[u],low[v]);
  34. if(low[v]>=t[u]) {
  35. int t;
  36. cnt=0;
  37. do {
  38. t=stk[top--];
  39. tmp[++cnt]=t;
  40. } while(t!=v);
  41. rep(i,1,cnt) E[u].pb(tmp[i]),cirsz[tmp[i]]=cnt+1,cirid[tmp[i]]=i+1; //处理sz
  42. }
  43. } else cmin(low[u],t[v]);
  44. }
  45. }
  46. ll Sum[N][N]; // 对于每一个sz,i的常数项预处理
  47. void dfs2(int u) {
  48. for(int v:E[u]) {
  49. sz[v]=sz[u]+cirsz[v]-1;
  50. int x=cirid[v]-2,y=cirsz[v]-cirid[v]; // x即len1,y即len2
  51. rep(i,0,sz[v]) dp[v][i]=0;
  52. rep(i,0,sz[u]) if(dp[u][i]) {
  53. dp[v][i+x]=dp[v][i+x]+dp[u][i];
  54. Mod1(dp[v][i+x]);
  55. dp[v][i+y]=dp[v][i+y]+dp[u][i];
  56. Mod1(dp[v][i+y]);
  57. dp[v][i]=dp[v][i]-dp[u][i];
  58. Mod2(dp[v][i]);
  59. }
  60. dfs2(v);
  61. }
  62. drep(i,sz[u],0) if(dp[u][i]) ans=(ans+dp[u][i]*Sum[sz[u]][i])%P; // 统计答案
  63. }
  64. int main(){
  65. //freopen("cactus.in","r",stdin);
  66. n=rd(),m=rd();
  67. rep(i,1,m) {
  68. int u=rd(),v=rd();
  69. G[u].pb(v),G[v].pb(u);
  70. }
  71. Inv[0]=Inv[1]=Fac[0]=Fac[1]=1;
  72. rep(i,2,n) {
  73. Inv[i]=(P-P/i)*Inv[P%i]%P;
  74. Fac[i]=Fac[i-1]*i%P;
  75. }
  76. rep(i,1,n) Inv[i]=Inv[i]*Inv[i-1]%P;
  77. rep(i,0,n) rep(j,C[i][0]=1,i) C[i][j]=C[i-1][j-1]+C[i-1][j],Mod1(C[i][j]);
  78. rep(i,0,n) rep(j,0,i) {
  79. rep(k,0,j) Sum[i][j]=(Sum[i][j]+Fac[k]*Fac[i-k]%P*C[j][k])%P;
  80. Sum[i][j]=(Sum[i][j]%P*Inv[i+1])%P;
  81. }
  82. rep(i,1,n) {
  83. dfn=0;
  84. rep(j,1,n) t[j]=0,E[j].clear();
  85. dfs1(i,0);
  86. sz[i]=0,dp[i][0]=1;
  87. dfs2(i);
  88. }
  89. ans=(ans%P+P)%P;
  90. printf("%lld\n",ans);
  91. }

LOJ 6240. 仙人掌的更多相关文章

  1. @loj - 2250@ 「ZJOI2017」仙人掌

    目录 @题目描述@ @solution@ @accepted code@ @details@ @题目描述@ 如果一个无自环无重边无向连通图的任意一条边最多属于一个简单环,我们就称之为仙人掌.所谓简单环 ...

  2. [loj 6496]「雅礼集训 2018 Day1」仙人掌

    传送门 Description 给出一张 \(n\)个点 \(m\)条边的无向连通图,其中每条边至多属于一个简单环,保证没有自环,可能有重边.你需要为其中每条边定向,其中第 \(i\)个点的出度不能超 ...

  3. LOJ #2547 Luogu P4517「JSOI2018」防御网络

    好像也没那么难写 LOJ #2547 Luogu P4517 题意 在一棵点仙人掌中等概率选择一个点集 求选出点集的斯坦纳树大小的期望 定义点仙人掌为不存在一个点在多个简单环中的连通图 斯坦纳树为在原 ...

  4. LOJ 2587 「APIO2018」铁人两项——圆方树

    题目:https://loj.ac/problem/2587 先写了 47 分暴力. 对于 n<=50 的部分, n3 枚举三个点,把图的圆方树建出来,合法条件是 c 是 s -> f 路 ...

  5. bzoj1023: [SHOI2008]cactus仙人掌图

    学习了一下圆方树. 圆方树是一种可以处理仙人掌的数据结构,具体见这里:http://immortalco.blog.uoj.ac/blog/1955 简单来讲它是这么做的:用tarjan找环,然后对每 ...

  6. 【BZOJ 1023】【SHOI 2008】cactus仙人掌图

    良心的题解↓ http://z55250825.blog.163.com/blog/static/150230809201412793151890/ tarjan的时候如果是树边则做树形DP(遇到环就 ...

  7. 【BZOJ-1952】城市规划 [坑题] 仙人掌DP + 最大点权独立集(改)

    1952: [Sdoi2010]城市规划 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 73  Solved: 23[Submit][Status][ ...

  8. 【BZOJ-4316】小C的独立集 仙人掌DP + 最大独立集

    4316: 小C的独立集 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 57  Solved: 41[Submit][Status][Discuss] ...

  9. 仙人掌(cactus)

    仙人掌(cactus) Time Limit:1000ms Memory Limit:64MB 题目描述 LYK 在冲刺清华集训(THUSC) !于是它开始研究仙人掌,它想来和你一起分享它最近研究的 ...

随机推荐

  1. spring的@primary和@qualifier注解解决一个接口多个实现的注入问题

    Spring中提供了@Primary和@Qualifier注解来解决一个接口多个实现的注入问题. @Primary注解 Spring中有提供一个@Primary注解,具体的作用是在一个接口有多个实现类 ...

  2. 一个简单的利用 WebClient 异步下载的示例(四)

    接上一篇,我们继续优化它. 1. DownloadEntry 类 public class DownloadEntry { public string Url { get; set; } public ...

  3. Kubernetes service 代理模式

    Kubernetes service 代理模式 底层流量转发与负载均衡实现:• Iptables(默认)• IPVS IPVS 了解代理模式之IPVS工作原理LVS 基于 IPVS内核调度模块实现的负 ...

  4. 【机器学习笔记】ID3构建决策树

    好多算法之类的,看理论描述,让人似懂非懂,代码走一走,现象就了然了. 引: from sklearn import tree names = ['size', 'scale', 'fruit', 'b ...

  5. RookeyFrame在线新增模块

    今天给大家演示下在线新增模块的功能,在线新增模块跟在vs中写model实体类区别不大,线上新增少了手动初始化的过程,新增后模块同样具备新增.修改.删除.查看.导入.导出.复制.批量编辑.回收站.草稿箱 ...

  6. Python【day 10】函数进阶-动态函数

    形参小结 1.位置参数2.默认值参数3.动态参数 1.*args 位置参数的动态传参. 系统会自动的把所有的位置参数聚合成元组 2.**kwargs 关键字参数的动态传参. 系统会自动的把所有的关键字 ...

  7. 手写instanceof (详解原型链) 和 实现绑定解绑和派发的事件类

    A  instanceof  B    是判断  A  是否继承自B,是返回true,  否返回false 再精确点就是判断B   是否  再  A  的 原型链上, 什么是原型链,举个例子: 我们定 ...

  8. 在使用 Fortify进行源码扫描时需要做对项目需要做什么?

    1.一般我们的项目都是svn 或git 进行管理的,为了扫出异常的问题 做好把   “” .svn    “”  文件删除 2.把我们的项目需要的jar 文件放到一个文件夹内同项目一起进行扫描.这样为 ...

  9. Android Activity之间的数据传递

    1.向目标Activity传递数据: Intent intent=new Intent(this,Main2Activity.class); //可传递多种类型的数据 intent.putExtra( ...

  10. 转:oracle 体系结构

    前几天面试的时候面试官才问过我ORACLE的体系结构,让我在一张白纸上画出来.回头想想当时答得还不错,大部分内容都描述出来了,呵呵,刚才在网上看到一篇讲解ORACLE体系结构的文章,觉得不错,转过来存 ...