题意

求基环树森林所有基环树的直径之和

题解

考虑的一个基环树的直径,只会有两种情况,第一种是某个环上结点子树的直径,第二种是从两个环上结点子树内的最深路径,加上环上这两个结点之间的较长路径。

那就找环,然后环上每个结点做树形\(dp\)。然后把环断成长度为\(2n\)的链,记录环上的前缀和\(sum\)。假设结点\(u\)子树内最深路径为\(dep[u]\),那么就是求\(max(sum[i] - sum[j] + dep[i] + dep[j]),j < i\)。这个就转换成\(max(sum[i] + dep[i] + dep[j] - sum[j])\),用单调队列维护最大的\(dep[j] - sum[j]\)就行,类似滑动窗口。

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std; typedef long long ll; const int N = 1e6 + 5; struct Edge {
int v, nxt, w;
} e[N << 1];
int hd[N], edge_num; void link(int u, int v, int w) {
e[edge_num] = (Edge) {v, hd[u], w};
hd[u] = edge_num ++;
} int n, loop[N], cnt, idx, dfn[N];
int fa[N], faw[N], loopw[N];
ll dep1[N], dep2[N], tree_d;
ll sum[N << 1], dep[N << 1];
bool onloop[N];
deque<int> q; void dfs(int u, int cur = -1) {
dfn[u] = ++ idx;
for(int i = hd[u]; ~ i; i = e[i].nxt) {
if(cur == i) continue ;
int v = e[i].v;
if(dfn[v]) {
if(dfn[v] > dfn[u] || cnt) continue ;
cnt ++;
loop[cnt] = u;
loopw[cnt] = e[i].w;
for(int j = u; j != v; j = fa[j]) {
cnt ++;
loop[cnt] = fa[j];
loopw[cnt] = faw[j];
}
} else fa[v] = u, faw[v] = e[i].w, dfs(v, i ^ 1);
}
} void dp(int u, int f = 0) {
dep1[u] = dep2[u] = 0;
for(int i = hd[u]; ~ i; i = e[i].nxt) {
int v = e[i].v;
if(v == f || onloop[v]) continue ;
dp(v, u);
ll dis = e[i].w + dep1[v];
if(dis > dep1[u]) {
dep2[u] = dep1[u];
dep1[u] = dis;
} else if(dis > dep2[u]) {
dep2[u] = dis;
}
}
tree_d = max(tree_d, dep1[u] + dep2[u]);
} int main() {
scanf("%d", &n);
fill(hd + 1, hd + n + 1, -1);
for(int i = 1, v, w; i <= n; i ++) {
scanf("%d%d", &v, &w);
link(v, i, w);
link(i, v, w);
}
ll ans = 0, d;
for(int i = 1; i <= n; i ++)
if(!dfn[i]) {
cnt = 0; dfs(i);
for(int j = 1; j <= cnt; j ++)
onloop[loop[j]] = 1;
d = 0;
for(int j = 1; j <= cnt; j ++) {
tree_d = 0;
dp(loop[j]);
dep[j] = dep[j + cnt] = dep1[loop[j]];
d = max(d, tree_d);
}
for(int j = 1; j <= cnt << 1; j ++)
sum[j] = sum[j - 1] + loopw[j <= cnt ? j : j - cnt];
q.clear();
for(int j = 1; j <= cnt << 1; j ++) {
//sum_j + dep_j + dep_i - sum_i
for(; !q.empty() && q.front() + cnt - 1 < j; q.pop_front()) ;
ll c = dep[j] - sum[j];
if(!q.empty()) d = max(d, sum[j] + dep[j] + dep[q.front()] - sum[q.front()]);
for(; !q.empty() && dep[q.back()] - sum[q.back()] <= c; q.pop_back()) ;
q.push_back(j);
}
ans += d;
}
printf("%lld\n", ans);
return 0;
}

什么,\(\text{BZOJ}\)上\(10^6\)会爆栈?那只能手工栈了。。

什么,手工栈会\(\text{MLE}\)?卡卡卡

然后代码就十分难看了(

\(block\)函数是把\(dfs\)到的结点的\(head\)复制一遍。

#include <algorithm>
#include <bitset>
#include <cstdio>
using namespace std; typedef long long ll; const int N = 1e6 + 5; struct Edge {
int v, nxt, w;
} e[N << 1];
int hd[N], h[N], edge_num; void link(int u, int v, int w) {
e[edge_num] = (Edge) {v, hd[u], w};
hd[u] = edge_num ++;
} int n, loop[N], loopw[N], cnt, idx, dfn[N];
int fa[N], faw[N], q[N << 1], node[N], qn, ql, qr;
ll tree_d, sum[N << 1], dep[N];
bitset<N> onloop, vis; void block(int s) {
ql = qr = qn = 0;
vis[s] = 1; q[qr ++] = s; node[qn ++] = s;
int u, i;
while(ql < qr) {
u = q[ql ++];
for(i = hd[u]; ~ i; i = e[i].nxt)
if(!vis[e[i].v]) {
vis[e[i].v] = 1;
q[qr ++] = e[i].v;
node[qn ++] = e[i].v;
}
}
for(i = 0; i < qn; i ++) vis[node[i]] = 0;
} int st[N][2], top;
void dfs(int s) {
block(s);
int i, j, u, v, cur;
for(i = 0; i < qn; i ++) h[node[i]] = hd[node[i]];
top = 0; st[top][0] = s; st[top][1] = -1; top ++;
while(top >= 1) {
u = st[top - 1][0]; cur = st[top - 1][1];
if(!dfn[u]) dfn[u] = ++ idx;
if(h[u] == -1) { top --; continue ; }
for(int &i = h[u]; ~ i; i = e[i].nxt) {
if(i == cur) continue ;
v = e[i].v;
if(!dfn[v]) {
fa[v] = u; faw[v] = e[i].w;
st[top][0] = v; st[top][1] = i ^ 1; top ++;
i = e[i].nxt;
break ;
} else if(dfn[v] < dfn[u]) {
cnt ++;
loop[cnt] = u;
loopw[cnt] = e[i].w;
onloop[u] = 1;
for(j = u; j != v; j = fa[j]) {
cnt ++;
loop[cnt] = fa[j];
loopw[cnt] = faw[j];
onloop[fa[j]] = 1;
}
}
}
}
} void block2(int s) {
ql = qr = qn = 0;
vis[s] = 1; q[qr ++] = s; node[qn ++] = s;
int u, i;
while(ql < qr) {
u = q[ql ++];
for(i = hd[u]; ~ i; i = e[i].nxt)
if(!vis[e[i].v] && !onloop[e[i].v]) {
vis[e[i].v] = 1;
q[qr ++] = e[i].v;
node[qn ++] = e[i].v;
}
}
for(i = 0; i < qn; i ++) vis[node[i]] = 0;
} ll dep1[N], dep2[N];
ll dp(int s) {
block2(s);
int i, j, u, v, cur; ll dis, dep2;
for(i = 0; i < qn; i ++) h[node[i]] = hd[node[i]];
top = 0; st[top][0] = s; st[top][1] = -1; top ++;
while(top >= 1) {
u = st[top - 1][0]; cur = st[top - 1][1];
if(h[u] == -1) {
dep2 = dep1[u] = 0;
for(int i = hd[u]; ~ i; i = e[i].nxt) {
if((v = e[i].v) == cur || onloop[v]) continue ;
dis = e[i].w + dep1[v];
if(dis > dep1[u]) {
dep2 = dep1[u];
dep1[u] = dis;
} else if(dis > dep2) {
dep2 = dis;
}
tree_d = max(tree_d, dep1[u] + dep2);
}
top --;
continue ;
}
for(int &i = h[u]; ~ i; i = e[i].nxt) {
if((v = e[i].v) == cur || onloop[v]) continue ;
st[top][0] = v; st[top][1] = u; top ++;
}
}
return dep1[s];
} int main() {
scanf("%d", &n);
fill(hd + 1, hd + n + 1, -1);
int i, j, v, w;
for(i = 1; i <= n; i ++) {
scanf("%d%d", &v, &w);
link(v, i, w); link(i, v, w);
}
ll ans = 0, d, c;
for(i = 1; i <= n; i ++)
if(!dfn[i]) {
cnt = d = 0; dfs(i);
for(j = 1; j <= cnt; j ++) {
tree_d = 0;
dep[j] = dp(loop[j]);
d = max(d, tree_d);
}
for(j = 1; j <= cnt << 1; j ++)
sum[j] = sum[j - 1] + loopw[j <= cnt ? j : j - cnt];
ql = qr = 0;
#define _dep(u) (u <= cnt ? dep[u] : dep[u - cnt])
for(j = 1; j <= cnt << 1; j ++) {
//sum_j + dep_j + dep_i - sum_i
for(; ql < qr && q[ql] + cnt - 1 < j; ql ++) ;
c = _dep(j) - sum[j];
if(ql < qr) d = max(d, sum[j] + _dep(j) + _dep(q[ql]) - sum[q[ql]]);
for(; ql < qr && _dep(q[qr - 1]) - sum[q[qr - 1]] <= c; qr --) ;
q[qr ++] = j;
}
#undef _dep
ans += d;
}
printf("%lld\n", ans);
return 0;
}

「BZOJ 1791」「IOI 2008」Island「基环树」的更多相关文章

  1. Solution -「基环树」做题记录

    写的大多只是思路,比较简单的细节和证明过程就不放了,有需者自取. 基环树简介 简单说一说基环树吧.由名字扩展可得这是一类以环为基础的树(当然显然它不是树. 通常的表现形式是一棵树再加一条非树边,把图画 ...

  2. 「BZOJ 3242」「NOI 2013」快餐店「基环树」

    题意 基环树上找到一个点(可以在边上)使得它到树上最远点的距离最小,输出最小距离 题解 如果是一棵树,答案就是树的直径\(/2\) 如果是基环树,那么很好证明删去环上的某一条边是不影响答案的.于是断环 ...

  3. BZOJ 1791: [IOI2008]Island 岛屿 - 基环树

    传送门 题解 题意 = 找出无向基环树森林的每颗基环树的直径. 我们首先需要找到每颗基环树的环, 但是因为是无向图,用tarjan找环, 加个手工栈, 我也是看了dalao的博客才知道tarjan找无 ...

  4. bzoj 2878: [Noi2012]迷失游乐园【树上期望dp+基环树】

    参考:https://blog.csdn.net/shiyukun1998/article/details/44684947 先看对于树的情况 设d[u]为点u向儿子走的期望长度和,du[u]为u点的 ...

  5. [IOI 2008] Island

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1791 [算法] 不难看出,要求的是这个基环树森林中每棵基环树的直径之和 [代码] # ...

  6. 「BZOJ 4228」Tibbar的后花园

    「BZOJ 4228」Tibbar的后花园 Please contact lydsy2012@163.com! 警告 解题思路 可以证明最终的图中所有点的度数都 \(< 3\) ,且不存在环长是 ...

  7. 「BZOJ 3645」小朋友与二叉树

    「BZOJ 3645」小朋友与二叉树 解题思路 令 \(G(x)\) 为关于可选大小集合的生成函数,即 \[ G(x)=\sum[i\in c ] x^i \] 令 \(F(x)\) 第 \(n\) ...

  8. 「BZOJ 4502」串

    「BZOJ 4502」串 题目描述 兔子们在玩字符串的游戏.首先,它们拿出了一个字符串集合 \(S\),然后它们定义一个字符串为"好"的,当且仅当它可以被分成非空的两段,其中每一段 ...

  9. 「BZOJ 4289」 PA2012 Tax

    「BZOJ 4289」 PA2012 Tax 题目描述 给出一个 \(N\) 个点 \(M\) 条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点 \(1\) 到点 \( ...

随机推荐

  1. filter中获取spring bean

    import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import ja ...

  2. mybatis 学习三 关键文件解析

    1:  mybatis-config.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYP ...

  3. 2015.1.8 Left join 左连接

    格式 select f1.a, f2.b form f1 left jion f2 on .... 注意:左边的查询部分只能有select和from,不能出现where order by等.若有必须在 ...

  4. redis使用测试

    import redis conn=redis.Redis(host='127.0.0.1',port=6379) conn.set('nn','morgana',10) #过期时间10s v=con ...

  5. Oracle中REGEXP_SUBSTR函数(字符串转多行)

    Oracle中REGEXP_SUBSTR函数 Oracle中REGEXP_SUBSTR函数的使用说明: 题目如下: 在oracle中,使用一条语句实现将'17,20,23'拆分成'17','20',' ...

  6. Linux 压缩文件 和解压文件

    .zip 解压:unzip FileName.zip 压缩:zip FileName.zip DirName .rar 解压:rar -x FileName.zip 压缩:rar -a FileNam ...

  7. mysql工具Navicat批量执行SQL语句

    例如:我现在要同时执行这么多语句 update community set xqmc=replace(xqmc,' ',''); update community set xqbm=replace(x ...

  8. 一些API的用法

    //1.init初始化 NSString * str1 = [[NSString alloc] init]; NSLog(@"str1 = %@",str1); //2.initW ...

  9. Composite模式 组合模式

    Android的ViewGroup 和 View 的关系,即是采用组合模式 1. 概述 在数据结构里面,树结构是很重要,我们可以把树的结构应用到设计模式里面. 例子1:就是多级树形菜单. 例子2:文件 ...

  10. Enumeration与Iterator的对比

    Enumeration与Iterator的对比 Enumeration 接口 Iterator 接口 参数的含义 枚举类型 迭代器元素类型 所在包 java.util 父类 无 子类 StringTo ...