仙人掌上DP问题

T1

题目链接

题解

在树上做是一个经典的DP,记 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]为 u u u子树内 u u u是否选的最大独立集,转移显然。在仙人掌上,还是先像树那样dfs,考虑每一条返祖边的影响,即对于每一个环在做一个dp,多记一维代表末尾是否选即可。注意dp的顺序,应保证每个点除了有伸向祖先的返祖边的子树外都考虑过,最后考虑有伸向祖先的返祖边的子树即可。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,hd[N],to[N],nx[N],tt=1;
void add(int u,int v){nx[++tt]=hd[u]; to[hd[u]=tt]=v;
}
int fa[N],dp[N],mn[N];
void pre(int u,int fl){for(int e=hd[u];e;e=nx[e]) if(e!=fl && (e^1)!=fl){int v=to[e];if(dp[v]){mn[u]=min(mn[u],dp[v]);continue;}dp[v]=mn[v]=dp[u]+1; fa[v]=u;pre(v,e); mn[u]=min(mn[u],mn[v]);}
}
int f[N][2],g[N][2][2];
void dfs(int u,int fl){int w=0,sn=0,sf=0; f[u][0]=0; f[u][1]=1;for(int e=hd[u];e;e=nx[e]) if(e!=fl && (e^1)!=fl){int v=to[e];if(dp[v]!=dp[u]+1){if(dp[v]<dp[u]) w=v;continue;}if(mn[v]<dp[u]){sn=v,sf=e; continue;}dfs(v,e);if(mn[v]>dp[u]){f[u][0]+=max(f[v][0],f[v][1]);f[u][1]+=f[v][0];}}if(sn) dfs(sn,sf);if(w){for(int p=0;p<2;p++) g[u][p][p]=f[u][p],g[u][p][!p]=-N;int y=u,x=fa[u]; while(1){for(int p=0;p<2;p++)g[x][0][p]=f[x][0]+max(g[y][0][p],g[y][1][p]),g[x][1][p]=f[x][1]+g[y][0][p];if(x==w){f[x][0]=max(g[x][0][0],g[x][0][1]);f[x][1]=g[x][1][0];break;}y=x; x=fa[x];}}
}
int main()
{cin>>n>>m;for(int u,v,i=1;i<=m;i++)scanf("%d%d",&u,&v),add(u,v),add(v,u);dp[1]=mn[1]=1; pre(1,0);dfs(1,0);cout<<max(f[1][0],f[1][1])<<endl;return 0;
}

T2

题目链接

题解

和上一题类似的套路,注意在这里没有必要像原本的树上dp那样记 u u u开头最长链和子树直径,只记一个以 u u u开头的最长链即可,答案在每个点的时候都算一下,这样比较简便。在环上的时候,对环顶dp值直接枚举端点算环上最短距离即可,对答案的贡献则需要枚举两个端点并考虑两点的最短距离,分情况讨论,拿单调队列维护即可。
至于dp顺序,至今不知道为什么像上一题那样会挂,调了不知道多久自闭了。后来改成网上其它博客的写法才过,以后还是这种写法吧,也比较简洁(感觉我和tarjan有仇啊,每次觉得很对都莫名其妙挂掉)。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,hd[N],to[N],nx[N],tt=1;
void add(int u,int v){nx[++tt]=hd[u]; to[hd[u]=tt]=v;
}
int fa[N],fl[N],dp[N],mn[N];
int f[N],ans,p[N],m,q[N];
void cnt(int w,int u){m=0; for(int i=u;i!=w;i=fa[i]) p[++m]=f[i];p[++m]=f[w]; reverse(p+1,p+m+1);int ft=0,tl=0,mx=0,l=m/2;for(int i=1;i<=m;i++){while(ft<tl && q[ft+1]+l<i) ft++;if(ft<tl) ans=max(ans,p[i]+i+p[q[ft+1]]-q[ft+1]);if(i>l+1){int x=i-l-1;mx=max(mx,x+p[x]);ans=max(ans,p[i]+m-i+mx);}while(ft<tl && p[q[tl]]-q[tl]<=p[i]-i) tl--;q[++tl]=i;}for(int i=2;i<=m;i++) f[w]=max(f[w],p[i]+min(i-1,m-i+1));
}
void dfs(int u){for(int e=hd[u];e;e=nx[e]) if(e!=fl[u] && (e^1)!=fl[u]){int v=to[e];if(!dp[v]){fl[v]=e; fa[v]=u; mn[v]=dp[v]=dp[u]+1; dfs(v);if(mn[v]>dp[u])ans=max(ans,f[u]+f[v]+1),f[u]=max(f[u],f[v]+1);mn[u]=min(mn[u],mn[v]);}else mn[u]=min(mn[u],dp[v]);}for(int e=hd[u];e;e=nx[e]) if(e!=fl[u] && (e^1)!=fl[u]){int v=to[e];if(fl[v]!=e && fl[v]!=(e^1) && dp[v]>dp[u]) cnt(u,v);}
}
int tp[N],t;
int main()
{cin>>n>>t;while(t--){int k; scanf("%d",&k);for(int i=1;i<=k;i++) scanf("%d",&tp[i]);for(int i=1;i<k;i++) add(tp[i],tp[i+1]),add(tp[i+1],tp[i]);}mn[1]=dp[1]=1; dfs(1);cout<<ans<<endl;return 0;
}

T3

题目链接

题解

还是一样的套路,先考虑在树上,设 f [ u ] f[u] f[u]为 u u u子树内所有关键点都联通到 u u u的距离和,转移显然。考虑在环上,需要考虑的就是环上有大于一个的子树有关键点。考虑如果已知一种方案有哪些点子树有关键点,可根据它在环上相邻的最大距离来计算贡献,故记 g [ i ] [ j ] [ k ] g[i][j][k] g[i][j][k]为起点 i i i,终点 j j j,中间最大空位为k的方案数、距离和,前缀和优化转移即可做到 O ( n 3 ) O(n^{3}) O(n3)。
upd:
其实根本没必要那么麻烦,一开始陷入套路就自闭了半天。可以直接不用DP,考虑每条边的贡献,树边直接算,环边再用上面的方法,也不需要记录代价和,直接记方案数即可。(我是个智障)

#include<bits/stdc++.h>
using namespace std;
const int N=205,M=1005,P=1e9+7;
int n,hd[N],to[M],nx[M],tt;
void add(int u,int v){nx[++tt]=hd[u]; to[hd[u]=tt]=v;
}
int fa[N],dp[N],mn[N],sz[N],pw[N],iv[N],ans;
int m,q[N],h[N][N],sh[N][N];
void cnt(int u,int v){m=0; int ct=0;for(int i=v;i!=u;i=fa[i])q[++m]=(pw[sz[i]]+P-1)%P,ct+=sz[i];sz[u]+=ct; q[++m]=(pw[n-ct]+P-1)%P; reverse(q+1,q+m+1);for(int x=1;x<m;x++){memset(h,0,sizeof(h)),memset(sh,0,sizeof(sh));for(int i=1;i<=m-x;i++) h[x][i]=q[x]; sh[x][1]=q[x];for(int i=x+1;i<=m;i++){for(int j=1;j<=i-x;j++){int w=((sh[i-1][j]+P-sh[i-j][j])%P+h[i-j][j])%P;h[i][j]=1ll*w*q[i]%P;sh[i][j]=(sh[i-1][j]+h[i][j])%P;if(i>x && j)ans=(ans+1ll*h[i][j]*(m-max(x+m-i,j))%P)%P;if(j) (h[i][j]+=h[i][j-1])%=P;}for(int j=i-x+1;j<=m-x;j++)h[i][j]=h[i][j-1];}}
}
void dfs(int u){sz[u]=1;for(int e=hd[u];e;e=nx[e]) if(to[e]!=fa[u]){int v=to[e];if(!dp[v]){fa[v]=u; dp[v]=mn[v]=dp[u]+1;dfs(v); mn[u]=min(mn[u],mn[v]);if(mn[v]==dp[u]+1)ans=(ans+1ll*(pw[sz[v]]-1)*(pw[n-sz[v]]-1)%P)%P,sz[u]+=sz[v];}else mn[u]=min(mn[u],dp[v]);}for(int e=hd[u];e;e=nx[e]) if(to[e]!=fa[u]){int v=to[e];if(fa[v]!=u && dp[v]>dp[u]) cnt(u,v);}
}
int main()
{int t; cin>>n>>t;pw[0]=1; for(int i=1;i<=n;i++) pw[i]=pw[i-1]*2%P;iv[0]=1; iv[1]=(P+1)/2;for(int i=2;i<=n;i++) iv[i]=1ll*iv[i-1]*iv[1]%P;for(int u,v,i=1;i<=t;i++)scanf("%d%d",&u,&v),add(u,v),add(v,u);mn[1]=dp[1]=1; dfs(1);cout<<1ll*ans*iv[n]%P<<endl;return 0;
}

圆方树

T4

题目链接

题解

考虑转化成树上问题,那么显然需要构建圆方树。原点和方点的距离如何定义呢?考虑在仙人掌上两个点找最短距离的过程,不考虑返祖边的话就是一直跳到lca,返祖变的作用就是在那个环上的时候有机会更快跳到环顶。于是对于每条返祖边对应的环,环上所有点到方点的距离先定位它到环顶端的最短距离。这样,如果两个点的lca是原点,那么直接计算;否则说明它们的lca在一个环上,多记录一下每个点在环上的编号即可。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10,M=17;
int n,m,q,c,t=1;
int h[N],to[N*2],nx[N*2],w[N*2];
void add(int u,int v,int x){t++; to[t]=v; nx[t]=h[u]; h[u]=t; w[t]=x;
}
int dn[N],ds[N],l[N],fa[N],fl[N];
bool vi[N*2],is[N*2];
void tar(int u){dn[u]=++c;for(int e=h[u],v;e;e=nx[e]){v=to[e]; if(vi[e]) continue; vi[e]=vi[e^1]=1;if(!dn[v]) ds[v]=ds[u]+w[e],fa[v]=u,fl[v]=e,tar(v);else if(dn[v]<dn[u]){n++; l[n]=ds[u]-ds[v]+w[e];add(n,v,0),add(v,n,0); is[e]=is[e^1]=1;for(int i=u;i!=v;i=fa[i]){int x=min(ds[i]-ds[v],l[n]-(ds[i]-ds[v]));add(i,n,x); add(n,i,x); is[fl[i]]=is[fl[i]^1]=1;}}}
}
int dp[N],dis[N],f[N][M];
void dfs(int u){for(int i=1;i<M;i++) f[u][i]=f[f[u][i-1]][i-1];for(int v,e=h[u];e;e=nx[e]) if(!is[e] && to[e]!=f[u][0]) v=to[e],dp[v]=dp[u]+1,dis[v]=dis[u]+w[e],f[v][0]=u,dfs(v);
}
int find(int u,int v){ //cout<<u<<" "<<v<<endl;if(dp[u]<dp[v]) swap(u,v);for(int i=M-1;~i;i--) if(dp[f[u][i]]>=dp[v]) u=f[u][i]; //cout<<u<<" "<<v<<endl;for(int i=M-1;~i;i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];//cout<<i<<endl;return (u!=v)?f[u][0]:u;
}
int get(int u,int v){for(int i=M-1;~i;i--) if(dp[f[u][i]]>dp[v]) u=f[u][i];return u;
}
int main()
{//freopen("G.in","r",stdin);//freopen(".out","w",stdout);cin>>n>>m>>q; int u,v,x;for(int i=1;i<=m;i++){scanf("%d %d %d",&u,&v,&x); add(u,v,x),add(v,u,x);}m=n; tar(1); dp[1]=1; dfs(1);/*for(int i=1;i<=n;i++){cout<<i<<" dis="<<dis[i]<<" dp="<<dp[i]<<" f0="<<f[i][0]<<" f1="<<f[i][1]<<endl;}*/while(q--){scanf("%d %d",&u,&v);int lca=find(u,v); //cout<<lca<<endl;if(lca<=m) printf("%d\n",dis[u]+dis[v]-2*dis[lca]);else{ //cout<<1<<endl;int fu=get(u,lca),fv=get(v,lca);int ans=dis[u]-dis[fu]+dis[v]-dis[fv];int len=abs(ds[fu]-ds[fv]);printf("%d\n",ans+min(len,l[lca]-len));}}return 0;
}

仙人掌问题 学习笔记相关推荐

  1. 【Cactus仙人掌图】仙人掌DP学习笔记

    我们从例题入手来考虑仙人掌上DP的一般规律叭. Ex 1.仙人掌上的单源最短路问题 联想树上最短路,由于路径的唯一性可以直接做一遍O(n)的搜索.但是仙人掌上显然不具备路径的唯一性这种性质. 那么我们 ...

  2. [学习笔记]多项式与有标号简单图计数

    学了一天的有标号无向图计数真的自闭了- 本篇文章是基于2019WC汪乐平大佬的讲课课件<生成函数,多项式算法与图的计数>编写的. 注意:文中所有生成函数都规定为指数型生成函数(EGF),请 ...

  3. 英语学习笔记(经典100句)

    [写在前面] 参考链接: 7000雅思词汇用100个句子记完!就是这么神奇! - 知乎 (zhihu.com) 本文所有的例句,来自上述链接,可直接看原文. 我这里只是学习笔记,供个人使用.如侵,请告 ...

  4. PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 call

    您的位置 首页 PyTorch 学习笔记系列 PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 发布: 2017年8月4日 7,195阅读 ...

  5. 容器云原生DevOps学习笔记——第三期:从零搭建CI/CD系统标准化交付流程

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  6. 容器云原生DevOps学习笔记——第二期:如何快速高质量的应用容器化迁移

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  7. 2020年Yann Lecun深度学习笔记(下)

    2020年Yann Lecun深度学习笔记(下)

  8. 2020年Yann Lecun深度学习笔记(上)

    2020年Yann Lecun深度学习笔记(上)

  9. 知识图谱学习笔记(1)

    知识图谱学习笔记第一部分,包含RDF介绍,以及Jena RDF API使用 知识图谱的基石:RDF RDF(Resource Description Framework),即资源描述框架,其本质是一个 ...

最新文章

  1. mycheckpoint
  2. 使用Mongo索引需要注意的几个点
  3. 【Python】 数字求和
  4. 一文读懂VictoriaMetrics集群方案
  5. 从入门到入土:基于C语言采用UDP协议实现通信功能的程序
  6. gpu云服务器运行游戏_追求高性能游戏,高帧高画质运行,你的手机需要多强的GPU才够?...
  7. 学校与工作(献于在校大学生及入职不久的工作者)
  8. Java图书管理系统(控制台程序)
  9. android学习资料整理-----高级篇
  10. 怎样裁剪PDF文件中的页面
  11. qq游戏大厅 for linux,网友第一时间抢先评测:腾讯QQ Linux版
  12. linux系统如何修复分区工具,如何恢復LINUX的硬盘分区表
  13. SpringBoot 生成带水印pdf | 中文字体问题
  14. 哪种程序员最挣钱?平均月薪30.8K,网友说这是掌握世界的技术!
  15. GEE开发之Modis_LST地表温度数据分析
  16. 5A学友的备考心得 | PMP考试如何一把过?
  17. 人工智能AI工程师职业规划指南
  18. ESP8266-Arduino编程实例-LIS3MDL磁场传感器驱动
  19. HDOJ 5144 NPY and shot 简单物理
  20. TapTap物理画线游戏,使用Unity实现2D物理画线功能

热门文章

  1. Mysql5.7版本的下载与安装
  2. 【3D目标检测】PV-RCNN
  3. pc 电脑web浏览器js通过usb控制安卓手机打电话挂电话(补充效果录像图)
  4. IOS 关于UINavigationBar的使用
  5. StatCounter
  6. 一元线性回归的MATLAB编程实现
  7. 知识图谱系列之neo4j入门
  8. Python语言学习:Python语言学习之正则表达式常用函数之re.search方法【输出仅一个匹配结果(内容+位置)】、re.findall方法【输出所有匹配结果(内容)】案例集合之详细攻略
  9. 24考研,11408,想在这里记录一下自己接下来一年备考的心路历程
  10. java swing 管理系统源码 网盘_java swing 图书管理系统(含数据库脚本)