动态圆方树(LCC)已弃疗 四月也应该要退役了 是OI的谎言

大半天没有一个正经点的教程的 不过这也不是个正经东西 比较冷门

那啥 猫某的仙人掌的课件放这了 提取码: 8gtq 里面讲的很清楚了 这里还有一个

好了 相信大家都懂了

第一阶段(仙人掌图)

初识仙人掌 主要根据定义乱搞

例如说 这道 题目 (网址不同)

如果 dp 的话 首先考虑树上最长距离怎么求

方法一:树形 dp 使 f [p][0/1] 表示到 p 点往下能走的最长距离和第二长的距离 然后枚举

然而并没有什么用 如果最长的路和第二长的路 是经过了同一仙人掌的两条不同下去再合起来的

0ms 就 GG 了........

方法二:随便一点遍历到与它距离最远的点 再从该点遍历到离该点最远的点

但是图上行不通!例如这个图 (专门 YY 了个很漂漂的仙人掌出来)

花了我好久搞的图片

于是你会惊奇地发现 如果我们从 天蓝色 的点 开始搜

你会跑到 粉粉 的点 那里

然后再跑到三个绿色的点之一 得出直径为 9

然而事实上直径应该是 两个 浅绿色 的点 长度为 10

可以看出 仙人掌这种东西着实毒瘤

方法三:针对仙人掌的环对其进行轰杀

嘛,不就出现了环么 那我们处处针对它

考虑改进方法一 方法二的话就算了,难道要每个仙人掌枚举删边吗 =-= 极端情况全是三点仙人掌 复杂度  算了

(话说我分析的复杂度是对的吗)

于是就开始像 树形 dp 一样 然后环的话 我开始用的是 裁半 的方法更新下去 像这样 结果.......

inline void update1(int p)
{for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (!is[b]) f[p] = max(f[p],f[b] + 1);
}
inline void update2(int p)
{ans1 = ans2 = 0;for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (!is[b]) update(f[b]);ans = max(ans,ans1 + ans2 + 1);
}
void getf(int p)
{stack[++t] = p;o[p] = 1;for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (dfn[b] > dfn[p] && !o[b]) getf(b);if (dfn[p] == low[p])if (stack[t] == p){--t;for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (dfn[b] > dfn[p]) f[p] = max(f[p],f[b] + 1);} else {memset(is,0,sizeof(is));int len = 0;while (stack[t] != p)cir[++len] = stack[t--],is[cir[len]] = 1; --t,is[p] = 1;update1(cir[(len >> 1) + 1]);update1(cir[len >> 1]);for (int i = (len >> 1) + 2,j = cir[i] ; i <= len ; ++ i,j = cir[i])for (int a = first[j],b = e[a].to ; a ; a = e[a].ne,b = e[a].to){if (is[b] && dfn[b] < dfn[j]) continue;f[j] = max(f[j],f[b] + 1);}for (int i = ((len + 1) >> 1) - 1,j = cir[i] ; i > 0 ; -- i,j = cir[i])for (int a = first[j],b = e[a].to ; a ; a = e[a].ne,b = e[a].to){if (is[b] && dfn[b] < dfn[j]) continue;f[j] = max(f[j],f[b] + 1);}for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to){if (dfn[b] < dfn[p]) continue;f[p] = max(f[p],f[b] + 1);}}
}
void getdp(int p)
{stack[++t] = p;o[p] = 1;for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (dfn[b] > dfn[p] && !o[b]) getdp(b);if (dfn[p] == low[p])if (stack[t] == p){--tot,ans1 = ans2 = 0;for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (dfn[b] > dfn[p]) update(f[b]);ans = max(ans,ans1 + ans2 + 1);} else {memset(is,0,sizeof(is));int len = 0;while (stack[t] != p)cir[++len] = stack[t--],is[cir[len]] = 1; --t,is[p] = 1;update2(cir[(len >> 1) + 1]);update2(cir[len >> 1]);for (int i = (len >> 1) + 2,j = cir[i] ; i <= len ; ++ i,j = cir[i]){ans1 = ans2 = 0;for (int a = first[j],b = e[a].to ; a ; a = e[a].ne,b = e[a].to){if (is[b] && dfn[b] < dfn[j]) continue;update(f[b]);}ans = max(ans,ans1 + ans2 + 1);}for (int i = ((len + 1) >> 1) - 1,j = cir[i] ; i > 0 ; -- i,j = cir[i]){ans1 = ans2 = 0;for (int a = first[j],b = e[a].to ; a ; a = e[a].ne,b = e[a].to){if (is[b] && dfn[b] < dfn[j]) continue;update(f[b]);}ans = max(ans,ans1 + ans2 + 1);}ans1 = ans2 = 0;for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (dfn[b] > dfn[p]) update(f[b]);ans = max(ans,ans1 + ans2 + 1);}
}

于是 因为一些毒瘤的路径重复原因 调了两个 20 节点的数据 过程都炸掉了

但居然还有 30 分 我的 4.4K Bytes 果然还是没有白打的

然后颓了好几个月没搞 再回来的时候已经崩溃了 (题目的原因加上生活中的原因)

当然我知道上面几行 和 我的 l j 代码 诸君是不想看的

转战 正经 dp 1.7K Bytes 诸君放心

首先我们深知这种图遍历时 不像普通图一样可以到处乱撞

如果它在环里 它不会跑到另一个环里遍历两个及以上的点 可以理解为不相关

如果不在环里 就像树一样 搜到头就回来了

因此我们使用无向图 Tarjan 即可以充分判断

话说无向图 Tarjan 只需要在有向图的程序里面加上 判断父亲 即可 如果有需要就用数组 没有就放子程序里

然后为了方便 我们顺便在遍历完图后 从最下面开始更新答案 像这样

void tarjan(int p) {dfn[p] = low[p] = ++tot;for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (b != fa[p]) {!dfn[b] ? fa[b] = p,dep[b] = dep[p] + 1,tarjan(b),low[p] = min(low[p],low[b]): low[p] = min(low[p],dfn[b]); //经典的Tarjan不解释了 dep是找环长用的if (low[b] > dfn[p]) ans = max(ans,dp[p] + dp[b] + 1),dp[p] = max(dp[p],dp[b] + 1);} b的连通分量的根大于p的搜索顺序 说明不在同一环中 照常更新for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (fa[b] != p && dfn[p] < dfn[b]) getcir(p,b); //这里搜到环了就直接搞环
} //从下往上更新答案 无后效性

处理环的话就是一次性把一整个环都搞出来 在这之前我们必然保证 dfn 比该环大的点 都已经更新完了 因为是 dfs

无后效性嘛 好了 那我们泰然自蒻地进入处理毒瘤环的环节

边长必定为 1 这个很好办了

为了不每个点绕一圈更新答案 我们考虑单调队列优化

因为最优的答案更新肯定小于半个环 我们不看环下面连的点 我们走环到环上点肯定是一半就能到两边了 所以把环断了再复制上一倍 每次更新半个环

子程序里面就只有复制环和单调队列了 队列里面也就是更新下答案 就不注释了

反正连我都懂的你们不可能看不懂是不是 所以下放这段的代码

void getcir(int x,int y) {int siz = dep[y] - dep[x] + 1,t = siz;for (int a = y ; a != x ; a = fa[a]) bot[t--] = dp[a]; bot[t] = dp[x];for (int a = 1 ; a <= siz ; ++ a) bot[a + siz] = bot[a];int l = 0,r = 0; que[0] = 1;for (int a = 2 ; a <= siz << 1 ; ++ a) {while (l <= r && a - que[l] > siz >> 1) ++ l;ans = max(ans,bot[a] + a + bot[que[l]] - que[l]);while (l <= r && bot[que[r]] - que[r] < bot[a] - a) -- r;que[++r] = a;}for (int a = 2 ; a <= siz ; ++ a) dp[x] = max(dp[x],bot[a] + min(a - 1,siz - a + 1));
}

好了 下面就放完整代码了 至于题目输入什么的 也不注释了 这个太无脑了 =-=

这道 题目 (网址不同) 的代码再放一下 =w=

#include <cstdio>
#define N 50010
inline int r() {char q = getchar(); int x = 0;while (q < '0' || q > '9') q = getchar();while ('0' <= q && q <= '9') x = x * 10 + q - 48,q = getchar();return x;
}
struct edge{int to,ne;}e[200010];
int first[N],dfn[N],low[N],dep[N],bot[N << 1],que[N],fa[N],dp[N],tot,ans;
inline int max(int x,int y) {return x > y ? x : y;}
inline int min(int x,int y) {return x < y ? x : y;}
void add(int x,int y) {e[++tot].ne = first[x],e[tot].to = y,first[x] = tot;e[++tot].ne = first[y],e[tot].to = x,first[y] = tot;
}
void getcir(int x,int y) {int siz = dep[y] - dep[x] + 1,t = siz;for (int a = y ; a != x ; a = fa[a]) bot[t--] = dp[a]; bot[t] = dp[x];for (int a = 1 ; a <= siz ; ++ a) bot[a + siz] = bot[a];int l = 0,r = 0; que[0] = 1; //话说这里r和我的快读重名了不过并没有什么事(好像慢了点)for (int a = 2 ; a <= siz << 1 ; ++ a) {while (l <= r && a - que[l] > siz >> 1) ++ l;ans = max(ans,bot[a] + a + bot[que[l]] - que[l]);while (l <= r && bot[que[r]] - que[r] < bot[a] - a) -- r;que[++r] = a;}for (int a = 2 ; a <= siz ; ++ a) dp[x] = max(dp[x],bot[a] + min(a - 1,siz - a + 1));
}
void tarjan(int p) {dfn[p] = low[p] = ++tot;for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (b != fa[p]) {!dfn[b] ? fa[b] = p,dep[b] = dep[p] + 1,tarjan(b),low[p] = min(low[p],low[b]): low[p] = min(low[p],dfn[b]);if (low[b] > dfn[p]) ans = max(ans,dp[p] + dp[b] + 1),dp[p] = max(dp[p],dp[b] + 1);}for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (fa[b] != p && dfn[p] < dfn[b]) getcir(p,b);
}
int main() {int n = r(),m = r();for (int b,x,k ; m > 0 ; -- m) {k = r(),x = r(); while (--k)b = r(),add(x,b),x = b;} tot = 0,tarjan(1),printf("%d\n",ans);return 0;
}

第二阶段(圆方树)

好了好了圆方树搞定了 打程序全程靠概念 yy 加上各种判断 感觉自己打的很毒瘤就是了 不过也就 4KBytes 不到

然后题目链接放这里了 又是板子题

感谢 @Harry_bh 提供的 hack 数据 让我受益匪浅emmm

话说打圆方树我用的是树剖嘛 像我这种会树剖不会倍增 会线段树不会树状数组的 实在是很少见了

而且我的代码太奇怪了感觉肯定很有问题 可能是乱讲一通

构造

搬一下 ImmortalCO 的课件里面的几句话

考虑为边设定边权,先随便取一个圆点当根,所有圆圆边的边权和 原图中一致

对于每一条圆方边: 如果它是方点的父边,则定义它的边权为 0,否则定义其边权为 「这个圆点到方点的父亲的最短路的长度」

现在,如果两点的 LCA 是圆点,则两点的最短路就是两点的圆方树上带权距离(所有环都在已经决定了走较短一侧)

否则,我们还需要考虑 LCA 这个环走哪一侧,用树链剖分或倍增 求出询问的两个点分别是在这个方点的哪两个子树中(即求出是环上的哪两个点),然后环上取较短的一侧

这里我参(zhao)照(ban)了之前那道 仙人掌图II 的遍历方法 然后在找到环的时候 将其 环权值 赋值到 即将连接的方点上

我们没必要每个点转一次环环 沿环遍历的同时 我们是从一边到达该点的 记录下来 循环完一圈后整个环的权值也记录下来了 然后再一个点一个点将 之前那段路的权值 和 环减去那段路的值 取 min 即可

下放一下这段代码

void getcir(int x,int y) {int len = tep[y] - tep[x] + 1,t = len; //len记录环上点数 t是指针ll lon = 0; //lon记录当前走的边的权值和for (int p = y ; p != x ; p = ta[p]) bot[t--] = p; bot[t] = x; //通过父亲数组取点for (int h = 1 ; h < len ; )for (int a = est[bot[h]],b = e[a].to ; ; a = e[a].ne,b = e[a].to)if (b == bot[h + 1]) {lon = lon + e[a].v,dis[++h] = lon; break;}for (int a = est[y],b = e[a].to ; ; a = e[a].ne,b = e[a].to)if (b == x) {lon = lon + e[a].v; break;} //搜环权值cir[++m] = lon; //记录环权值for (int a = 1 ; a <= len ; ++ a) addf(bot[a],m,min(lon - dis[a],dis[a]));
} //(上面)圆方加边
void tarjan(int p) { //用tarjan找环dfn[p] = low[p] = ++tnt;for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (b != ta[p]) {!dfn[b] ? tep[b] = tep[p] + 1,ta[b] = p,tarjan(b),low[p] = min(low[p],low[b]): low[p] = min(low[p],dfn[b]);if (low[b] > dfn[p]) addf(p,b,e[a].v);}for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (ta[b] != p && dfn[p] < dfn[b]) getcir(p,b);
}

那么构造说完了 我们来讲

建树

@142857cs 提供了树上前缀和的思路 感觉很好但我太顽固了实在是不想打 于是树剖加上线段树存权值

就是树剖的两个 dfs 啦 不过注意在 dfs1里要加上边权化点权 如果不会树剖边化点的可以去看看这个板子题

直接放代码了 这是两个深搜

void dfs1(int p) {dep[p] = dep[fa[p]] + 1,++siz[p];for (int a = fst[p],b = f[a].to ; a ; a = f[a].ne,b = f[a].to)if (b != fa[p]) {v[b] = f[a].v,fa[b] = p,dfs1(b),siz[p] += siz[b];if (siz[son[p]] < siz[b]) son[p] = b;}
}
void dfs2(int p,int an) { //an就是ancestor了,由于一些重名的原因..top[p] = an;id[p] = ++tot;oid[tot] = p;if (!son[p]) return;dfs2(son[p],an);for (int a = fst[p],b = f[a].to ; a ; a = f[a].ne,b = f[a].to)if (b != fa[p] && b != son[p]) dfs2(b,b);
}

这是一个建树

void build(int l,int r,int len) {if (l == r) {tr[len] = v[oid[l]]; return;}int mid = (l + r) >> 1;build(l,mid,len << 1);build(mid + 1,r,len << 1 | 1);tr[len] = tr[len << 1 | 1] + tr[len << 1];
}

查询

查询着实是毒瘤

考虑方点圆点?不止!

这里再次感谢 @Harry_bh 让误入歧途的我改过自新步入正轨

因为方点下面跳的两个点可能是两轻边呢=-= 那么下面来讲讲

lca 为圆点

直接搜索搜完了看看顶上那个点序号是不是大于 n 就好了

lca 为方点

首先我们要减去方点连的两圆点的权值

因为点权记录的是这个圆点到方点的父亲的最短路的长度 我们最后又不一定要跑过去

然后下面来说说边的问题

如果两个都是轻边 那么可以通过前驱来记录

如果其中一个是重边 那么我们就要把那个什么的点的前驱改成 lca 的 重儿子 不然都不知道掉到哪里去了

那放一下代码 首先是求 lca 的

ll out(int x,int y) {int fx = x,fy = y;//前驱 这个不赋值都可以ll ans = 0;while (top[x] != top[y]) {if (dep[top[x]] < dep[top[y]]) swap(x,y),swap(fx,fy);ans += get(1,m,1,id[top[x]],id[x]);fx = top[x],x = fa[top[x]];} //跳树剖if (x != y) {if (dep[x] > dep[y]) swap(x,y),swap(fx,fy);ans += get(1,m,1,id[x] + 1,id[y]);} //记录终焉路径if (x <= n) return ans; //lca为圆点赶快退掉if (fy[fa] != fx[fa]) fy = son[x]; //把重链上的点提上来ans = ans - v[fx] - v[fy]; //减去多余路径if (tep[fx] > tep[fy]) swap(fx,fy); //这个因为父亲数组的原因要按dep排return ans + geft(fx,fy,cir[x]);
}

然后是 get 和 geft 这两个东西

get 就是线段树找连续一段的权值 这个树剖模板里有的 不多加阐述

geft 其实就是找两点的最短路径啦 某hkr 说这个也可以搞前缀记录 不过我太懒了 每个询问又跑了一遍环

所以应该是会被 hack 的 因为假如查询的环大 我这个要跑大半圈......

注释放代码里面吧

ll get(int l,int r,int len,int i,int j) {if (i <= l && r <= j) return tr[len];int mid = (l + r) >> 1; ll ans = 0;if (i <= mid) ans += get(l,mid,len << 1,i,j);if (mid < j) ans += get(mid + 1,r,len << 1 | 1,i,j);return ans;
}
ll geft(int x,int y,int cirdis) { //cirdis是环的总权值 之前tarjan的时候记录了的ll ans = 0;for (int p = y ; p != x ; p = ta[p]) //通过父亲找到两点距离for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (b == ta[p]) {ans = ans + e[a].v; break;}return min(cirdis - ans,ans); //取最小 因为两条路嘛
}

好了 接下来是总代码 随便加点注释吧

#include <algorithm>
#include <cstring>
#include <cstdio>
#define N 20010
#define M 100010
#define ll long long
#define swap std::swap
inline int re() {int x = 0; char w = getchar();while (w < '0' || w > '9') w = getchar();while ('0' <= w && w <= '9') x = x * 10 + w - 48,w = getchar();return x;
}
struct edge{int ne,to; ll v;}e[M],f[M];
int est[N],fst[N],dfn[N],low[N],tep[N],bot[N],ta[N],fr[N]; //这些是找环用的
int siz[N],son[N],dep[N],top[N],oid[N],fa[N],id[N]; //这些是树剖用的 名字比较像
int tr[N << 2],v[N];
int tot,tnt,n = re(),m = re(),q = re();
ll cir[N],dis[N];
template <typename T> T min(T x,T y) {return x < y ? x : y;}
void adde(int x,int y,int z) {e[++tot].ne = est[x],e[tot].to = y,e[tot].v = z,est[x] = tot;e[++tot].ne = est[y],e[tot].to = x,e[tot].v = z,est[y] = tot;
}
void addf(int x,int y,ll z) {f[++tot].ne = fst[x],f[tot].to = y,f[tot].v = z,fst[x] = tot;f[++tot].ne = fst[y],f[tot].to = x,f[tot].v = z,fst[y] = tot;
}
void getcir(int x,int y) {int len = tep[y] - tep[x] + 1,t = len;ll lon = 0;for (int p = y ; p != x ; p = ta[p]) bot[t--] = p; bot[t] = x;for (int h = 1 ; h < len ; )for (int a = est[bot[h]],b = e[a].to ; ; a = e[a].ne,b = e[a].to)if (b == bot[h + 1]) {lon = lon + e[a].v,dis[++h] = lon; break;}for (int a = est[y],b = e[a].to ; ; a = e[a].ne,b = e[a].to)if (b == x) {lon = lon + e[a].v; break;}cir[++m] = lon;for (int a = 1 ; a <= len ; ++ a) addf(bot[a],m,min(lon - dis[a],dis[a]));
} //找环加方点 顺便记录环长度
void tarjan(int p) {dfn[p] = low[p] = ++tnt;for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (b != ta[p]) {!dfn[b] ? tep[b] = tep[p] + 1,ta[b] = p,tarjan(b),low[p] = min(low[p],low[b]): low[p] = min(low[p],dfn[b]);if (low[b] > dfn[p]) addf(p,b,e[a].v);}for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (ta[b] != p && dfn[p] < dfn[b]) getcir(p,b);
}
void dfs1(int p) {dep[p] = dep[fa[p]] + 1,++siz[p];for (int a = fst[p],b = f[a].to ; a ; a = f[a].ne,b = f[a].to)if (b != fa[p]) {v[b] = f[a].v,fa[b] = p,dfs1(b),siz[p] += siz[b];if (siz[son[p]] < siz[b]) son[p] = b;}
}
void dfs2(int p,int an) {top[p] = an;id[p] = ++tot;oid[tot] = p;if (!son[p]) return;dfs2(son[p],an);for (int a = fst[p],b = f[a].to ; a ; a = f[a].ne,b = f[a].to)if (b != fa[p] && b != son[p]) dfs2(b,b);
}
void build(int l,int r,int len) {if (l == r) {tr[len] = v[oid[l]]; return;}int mid = (l + r) >> 1;build(l,mid,len << 1);build(mid + 1,r,len << 1 | 1);tr[len] = tr[len << 1 | 1] + tr[len << 1];
}
ll get(int l,int r,int len,int i,int j) {if (i <= l && r <= j) return tr[len];int mid = (l + r) >> 1; ll ans = 0;if (i <= mid) ans += get(l,mid,len << 1,i,j);if (mid < j) ans += get(mid + 1,r,len << 1 | 1,i,j);return ans; //线段树查询链上权值和
}
ll geft(int x,int y,int cirdis) {ll ans = 0;for (int p = y ; p != x ; p = ta[p])for (int a = est[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)if (b == ta[p]) {ans = ans + e[a].v; break;}return min(cirdis - ans,ans);
} //找环上距离通过之前的ta数组找较深点的父亲跳上去 然后取最小值
ll out(int x,int y) {int fx = x,fy = y; //记前驱ll ans = 0;while (top[x] != top[y]) {if (dep[top[x]] < dep[top[y]]) swap(x,y),swap(fx,fy);ans += get(1,m,1,id[top[x]],id[x]);fx = top[x],x = fa[top[x]];} //跳树剖if (x != y) {if (dep[x] > dep[y]) swap(x,y),swap(fx,fy);ans += get(1,m,1,id[x] + 1,id[y]);}if (x <= n) return ans; //如果lca是圆点就跳出去if (fy[fa] != fx[fa]) fy = son[x];ans = ans - v[fx] - v[fy]; //如果是方点就去掉方点下面两点权值if (tep[fx] > tep[fy]) swap(fx,fy);return ans + geft(fx,fy,cir[x]); //跑环
}
int main() {for (int z,y,x ; m > 0 ; -- m) x = re(),y = re(),z = re(),adde(x,y,z);m = n,tot = 0,tarjan(1),dfs1(1),tot = 0,dfs2(1,1),build(1,m,1); //m到后来是存圆方树上点数的for (int y1,x1 ; q > 0 ; -- q) x1 = re(),y1 = re(),printf("%lld\n",out(x1,y1));return 0;
}

第三阶段(动态圆方树)

这个我不会,实在找不到正常点的标

VFleaKing的我看不懂啊 =-= 到时候问一下InFleaKing吧(汗

完结撒花

仙人掌 圆方树 || 静态 + 动态 (差动态)相关推荐

  1. P5236 【模板】静态仙人掌(仙人掌圆方树)

    无向仙人掌图 一般需要重构成 仙人掌有向树 然后我们 就考虑 圆点的 方点的不同讨论 这个题是查询两点间最短路 如果lca 是圆点 那么就是 d[a]+d[b]-2*d[lca] 如果是方点 我们需要 ...

  2. 仙人掌圆方树学习笔记

    终于对仙人掌有了一点初步的理解. 仙人掌 仙人掌是什么? 仙人掌是一个无向图. 仙人掌有什么特点? 仙人掌的每条边只属于一个简单环. 下面是一个栗子 有什么用呢? 我们可以先用\(tarjan\)找出 ...

  3. cactus仙人掌图【仙人掌圆方树+树形DP+单调队列】

    题目链接 BZOJ 1023 首先,圆方树是比较好想到的,维护直径,我们最方便的做法就是先让它变成一棵树,这里因为是仙人掌图,所以就用圆方树来构建. 再者,就是维护直径了,比较好想到的是非环上结点,就 ...

  4. [BZOJ2125]最短路(圆方树DP)

    题意:仙人掌图最短路. 算法:圆方树DP,$O(n\log n+Q\log n)$ 首先建出仙人掌圆方树(与点双圆方树的区别在于直接连割边,也就是存在圆圆边),然后考虑点u-v的最短路径,显然就是:在 ...

  5. 仙人掌与圆方树的学习 【模板】静态仙人掌

    题目链接 BZOJ 2125 最短路 圆方树 求一幅仙人掌图中,Q次询问两点最短路. 仙人掌问题,我们可以直接将原来的N个点缩点成为一棵生成树--圆方树. 这棵圆方树是怎样建立的呢,首先,我们看图: ...

  6. 洛谷 :P5236 【模板】静态仙人掌(圆方树模板 + 仙人掌最短路)

    题意很简单,在仙人掌图上求两点的最短路. 做法:需要用到圆方树 先来看看什么是圆方树:圆方树,就是由仙人掌图转化而来,树上分两种点:圆点和方点,圆点是仙人掌图上的点,方点是由仙人掌的环转化而来. 由于 ...

  7. 仙人掌相关问题的解法(1)-DFS树解决仙人掌DP问题,圆方树

    前言 有难度的仙人掌题在近几年也只是在国家集训队水平的比赛里才会出现. 不过,这不是说仙人掌对国集水平以下的选手意义不大: 首先,仙人掌暴力 DP 问题难度并不大,在省选. NOI 甚至 NOIP 中 ...

  8. bzoj4564: [Haoi2016]地图 仙人掌的圆方树 莫队 分块

    bzoj4564: [Haoi2016]地图 Description 一天rin来到了一个遥远的都市.这个都市有n个建筑,编号从1到n,其中市中心编号为1,这个都市有m条双向通 行的街道,每条街道连接 ...

  9. 仙人掌问题(圆方树)

    [算法简介] 仙人掌就是把树上多连了一些返祖边,构成了一些环 根据仙人掌这个名字我们也可以较为形象的感受到图的形态 具体的,仙人掌分为点仙人掌和边仙人掌,定义分别为点/边最多属于一个环 之所以把这样的 ...

  10. 5909. 【NOIP2018模拟10.16】跑商(圆方树+树链剖分+SET)

    题目大意: 基三的地图可以看做 n 个城市,m 条边的无向图,尊者神高达会从任意一个点出发并在起点购买货物,在旅途中任意一点卖出并最终到达终点,尊者神高达的时间很宝贵,所以他不会重复经过同一个城市,但 ...

最新文章

  1. 从零开始学习python:demo2.3
  2. Ubuntu 16.04安装Chrome浏览器
  3. [转]该学Java或.NET?
  4. 前端常用 JavaScript 方法封装
  5. python环境变量添加失败_python环境变量设置失败
  6. Cocos2d-x--开发参考资料
  7. 基于Jupyter完成Iris数据集的 Fisher线性分类,学习数据可视化技术
  8. 数据驱动的物流网络体系
  9. oracle的成本核算,请教一下ORACLE ERP制造成本核算(标准成本法)的优点,多谢了!...
  10. rms 文档权限服务器,Rms操作设置office系统文档权限
  11. php命名空间namespace应用
  12. 108-周跳探测之GF
  13. 离谱!诺奖得主被曝40多篇论文造假!还涉及国内高校学者!
  14. 剑指Offer——滴滴笔试题+知识点总结
  15. vue表单校验,根据某选项追加或去掉校验
  16. HDU 5594(ZYB's Prime-网络流)
  17. exchange2007+outlook2007,无法使用外出助理
  18. 线程池基本了解及其参数配置
  19. 学习Vue3 第三十四章(Vue3集成Tailwind CSS)
  20. Vue Admin Plus、Vue Shop Vite 官网演示地址文档

热门文章

  1. 桌面运维问题快速定位原因的常用基本思路
  2. Windows 10 按电源键只关屏幕不休眠的解决办法
  3. DirectX11海洋模拟实践
  4. 一朵花的组成结构图_花是由哪几个部分组成的?
  5. npm 包解析 eml 文件
  6. MacOS修改Hosts文件
  7. 如何判断欠拟合、适度拟合、过拟合
  8. 题解:艾米利亚的魔法
  9. 电源管理允许此设备唤醒计算机怎么关掉,允许计算机关闭此设备以节省电量灰色 | MOS86...
  10. matlab gui双音拨号,电子信息毕业设计---双音多频拨号系统的MATLAB仿真实现