做点双连通的时候发现很多题目可以用圆方树写
特意去学了一下,入门很简单嘛,而且是个很神奇的东西!!
前 置 知 识 : 点 双 连 通 分 量 , 没 了 \color{Red}前置知识:点双连通分量,没了 前置知识:点双连通分量,没了
大 概 就 是 把 一 般 图 变 成 树 的 结 构 , 然 后 我 们 就 可 以 在 上 面 大概就是把一般图变成树的结构,然后我们就可以在上面 大概就是把一般图变成树的结构,然后我们就可以在上面乱搞了
比 如 给 定 一 张 无 向 图 , 怎 么 把 它 转 化 为 树 ? 比如给定一张无向图,怎么把它转化为树? 比如给定一张无向图,怎么把它转化为树?
大 概 做 法 是 : 求 出 所 有 点 双 连 通 分 量 , 在 每 个 点 双 连 通 中 设 置 一 个 方 点 大概做法是:求出所有点双连通分量,在每个点双连通中设置一个\color{Red}{方点} 大概做法是:求出所有点双连通分量,在每个点双连通中设置一个方点
把 每 个 原 图 中 的 点 看 成 一 个 圆 点 把每个原图中的点看成一个\color{Red}圆点 把每个原图中的点看成一个圆点
然 后 每 个 点 双 连 通 分 量 中 的 点 向 新 设 的 对 应 的 方 点 连 边 然后每个点双连通分量中的点向新设的对应的方点连边 然后每个点双连通分量中的点向新设的对应的方点连边
去 掉 原 图 中 点 双 连 通 分 量 间 的 边 去掉原图中点双连通分量间的边 去掉原图中点双连通分量间的边
看起来很不直观对吧…在这里盗用一张别人的图,很清楚
如 图 所 示 , 无 向 图 经 过 设 置 方 点 和 圆 点 构 成 了 最 右 边 的 一 棵 树 如图所示,无向图经过设置方点和圆点构成了最右边的一棵树 如图所示,无向图经过设置方点和圆点构成了最右边的一棵树
但 是 这 棵 树 有 什 么 性 质 ? 看 一 道 例 题 吧 . . . . . . . 不 难 的 ( 仅 指 这 道 例 题 e m m ) 但是这棵树有什么性质?看一道例题吧.......不难的(仅指这道例题emm) 但是这棵树有什么性质?看一道例题吧.......不难的(仅指这道例题emm)
题意:给出无向图,求(s,c,f)三元组的个数,代表从s出发经过c达到f(不能重复经过点)
考 虑 建 好 的 圆 方 树 , 怎 么 来 写 呢 ? 考虑建好的圆方树,怎么来写呢? 考虑建好的圆方树,怎么来写呢?
如 果 固 定 了 s 和 f , 能 不 能 快 速 求 出 c 呢 ? 如果固定了s和f,能不能快速求出c呢? 如果固定了s和f,能不能快速求出c呢?
比如这张无向图
我 们 构 建 的 圆 方 树 是 这 样 的 ( 每 个 点 双 都 要 新 建 一 个 方 点 ! ! ) 我们构建的圆方树是这样的(每个点双都要新建一个方点!!) 我们构建的圆方树是这样的(每个点双都要新建一个方点!!)
现 在 如 果 s = 4 , f = 5 , c 有 几 种 可 能 ? 现在如果s=4,f=5,c有几种可能? 现在如果s=4,f=5,c有几种可能?
不 难 发 现 , c 可 以 取 的 值 有 2 , 3 , 1 , 6 不难发现,c可以取的值有2,3,1,6 不难发现,c可以取的值有2,3,1,6
仔细观察会发现这些点都在s到f在圆方树上路径的方点周围
这 是 很 重 要 的 性 质 这是很重要的性质 这是很重要的性质
如 果 我 们 给 方 点 赋 一 个 权 值 为 它 代 表 的 点 双 连 通 大 小 如果我们给方点赋一个权值为它代表的点双连通大小 如果我们给方点赋一个权值为它代表的点双连通大小
所 有 圆 点 赋 值 为 − 1 所有圆点赋值为-1 所有圆点赋值为−1
那 么 s 到 f 的 权 值 和 不 就 是 c 的 取 值 可 能 吗 ? 那么s到f的权值和不就是c的取值可能吗? 那么s到f的权值和不就是c的取值可能吗?
你 可 能 奇 怪 为 什 么 圆 点 赋 值 − 1 你可能奇怪为什么圆点赋值-1 你可能奇怪为什么圆点赋值−1
很 简 单 呢 , 图 中 节 点 2 被 上 下 两 个 方 点 包 含 了 ( 计 算 了 2 次 权 值 ) 很简单呢,图中节点2被上下两个方点包含了(计算了2次权值) 很简单呢,图中节点2被上下两个方点包含了(计算了2次权值)
如 果 给 圆 点 2 赋 值 − 1 就 能 很 好 解 决 这 个 问 题 如果给圆点2赋值-1就能很好解决这个问题 如果给圆点2赋值−1就能很好解决这个问题
但 是 回 到 题 目 , 没 有 给 定 s 和 t , 枚 举 复 杂 度 不 得 上 天 ? ? 但是回到题目,没有给定s和t,枚举复杂度不得上天?? 但是回到题目,没有给定s和t,枚举复杂度不得上天??
转变思维,s和t任意,代表我们应该计算圆方树中任意2个圆点间的权值和
等 价 于 统 计 每 个 点 被 经 过 了 x 次 , 乘 上 权 值 就 是 这 个 点 的 贡 献 嘛 ! ! 等价于统计每个点被经过了x次,乘上权值就是这个点的贡献嘛!! 等价于统计每个点被经过了x次,乘上权值就是这个点的贡献嘛!!
完 成 这 个 统 计 , 就 树 型 d p 啊 ! ! 完成这个统计,就树型dp啊!! 完成这个统计,就树型dp啊!!
至 于 怎 么 树 型 d p , 代 码 很 清 楚 , 不 再 累 赘 了 至于怎么树型dp,代码很清楚,不再累赘了 至于怎么树型dp,代码很清楚,不再累赘了
#include <bits/stdc++.h> using namespace std; #define int long long const int maxn=1e6+10; int n,m,ans; vector<int>vec[maxn]; struct edge{ int to,nxt; }d[maxn]; int head[maxn<<2],cnt=1; void add(int u,int v){ d[++cnt]=(edge){v,head[u]},head[u]=cnt;} void ins(int u,int v){ vec[u].push_back(v); } int low[maxn],dfn[maxn],stac[maxn],id,top; int square[maxn],nowsize,f,siz[maxn]; void tarjan(int u) { low[u]=dfn[u]=++id,stac[++top]=u; nowsize++; for(int i=head[u];i;i=d[i].nxt ) { int v=d[i].to; if( !dfn[v] ) { tarjan(v),low[u]=min( low[u],low[v] ); if( low[v]>=dfn[u] )//发现割点 { square[++f]=0; while( 1 ) { ins(f,stac[top]); ins(stac[top],f ); square[f]++; if( stac[top--]==v ) break; } ins(f,u); ins(u,f); square[f]++; } } else low[u]=min( low[u],dfn[v] ); } } void dfs(int u,int fa) { siz[u] = ( u<=n ); for(int i=0;i<vec[u].size();i++) { int v=vec[u][i]; if( v==fa ) continue; dfs(v,u); ans+=siz[v]*siz[u]*square[u]; siz[u]+=siz[v]; } ans+=siz[u]*( nowsize-siz[u] )*square[u]; } signed main() { cin >> n >> m; f=n;//方点编号从n+1起 for(int i=1;i<=n;i++) square[i]=-1; for(int i=1;i<=m;i++) { int l,r; cin >> l >> r; add(l,r); add(r,l); } for(int i=1;i<=n;i++) { if( dfn[i] ) continue; nowsize=0; tarjan(i),top--;//排除根节点 dfs(i,0); } cout << ans*2;//无序对,s和t可以交换 }