bzoj1791
1791: [Ioi2008]Island 岛屿
Time Limit: 20 Sec Memory Limit: 162 MB
Submit: 1680 Solved: 369
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
3 8
7 2
4 2
1 4
1 9
3 4
2 3
Sample Output
HINT

Source
这个图跟信息传递很像,也就是一个基环树,但是我很思博地没看出来。。。
事实上这是一个基环树森林,我们要求的是每棵基环树的直径(不太准确),也就是基环树上最长的路径
首先得找到环,这里我们可以分两种情况
1.这条路径在环上,那么也就是说两个端点在环上某个顶点的子树里或环上,那么这个样子可以这么计算:dis=d子树1+d子树2+d环,我们先把环拉开复制一遍接在后面,然后对于环上每个顶点的子树计算一下子树最深的地方到环上该点的距离,这些子树取个最大值。然后就可以dp了dp[i] = max(dp[i], d[i]-d[j]) 这里可以用单调队列优化,具体看代码
2.最长路径位于环上某个顶点的子树里,或者跨越某个顶点的两颗子树。这里就是一个树形dp,代码里写的不清楚,其实就是求树的直径的代码。
吐槽:写了4个小时,忽略了好几种情况,竟然还会爆栈,我不会写手工栈。。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = ;
struct edge {
int nxt, w, to;
} e[N << ];
vector<int> route;
int n, cnt = , pos;
ll ans, Max;
int used[N], vis[N], head[N], mark[N], w[N], prev[N], pree[N << ];
ll mx[N], d[N << ], g[N], q[N], dp[N];
inline int read()
{
int x = , f = ; char c = getchar();
while(c < '' || c > '') { if(c == '-') f = -; c = getchar(); }
while(c >= '' && c <= '') { x *= ; x += c - ''; c = getchar(); }
return x * f;
}
inline void link(int u, int v, int w)
{
e[++cnt].nxt = head[u];
head[u] = cnt;
e[cnt].to = v;
e[cnt].w = w;
}
void Dp(int u, int last)
{
ll Max1 = , Max2 = ;
for(int i = head[u]; i; i = e[i].nxt) if(e[i].to != last)
{
Dp(e[i].to, u);
if(g[e[i].to] + (ll)e[i].w >= Max1)
{
Max2 = Max1;
Max1 = g[e[i].to] + (ll)e[i].w;
}
else if(g[e[i].to] + (ll)e[i].w > Max2) Max2 = g[e[i].to] + (ll)e[i].w;
Max = max(Max, Max1 + Max2);
}
g[u] = Max1;
}
bool getcir(int u, int last)
{
vis[u] = ; bool flag = false;
for(int i = head[u]; i; i = e[i].nxt)
{
if(e[i].to == last && !flag) { flag = true; continue; }
if(!vis[e[i].to])
{
prev[e[i].to] = u; pree[e[i].to] = i;
if(getcir(e[i].to, u)) return true;
}
else { pree[e[i].to] = i; pos = u; prev[e[i].to] = u; return true; }
}
return false;
}
void dfs(int u)
{
used[u] = ;
for(int i = head[u]; i; i = e[i].nxt) if(!used[e[i].to]) dfs(e[i].to);
}
void solve(int u)
{
route.clear();
getcir(u, );
for(int now = pos; !mark[now]; now = prev[now])
{
mark[now] = ;
route.push_back(now); w[now] = e[pree[now]].w;
}
for(int i = ; i < route.size(); ++i)
{
ll Max1 = , Max2 = ;
for(int j = head[route[i]]; j; j = e[j].nxt) if(!mark[e[j].to])
{
Dp(e[j].to, route[i]);
if(g[e[j].to] + (ll)e[j].w >= Max1) { Max2 = Max1; Max1 = g[e[j].to] + e[j].w; }
else if(g[e[j].to] + e[j].w > Max2) Max2 = g[e[j].to] + e[j].w;
mx[route[i]] = max(mx[route[i]], g[e[j].to] + (ll)e[j].w);
dp[route[i]] = max(dp[route[i]], max(Max, Max1 + Max2)); Max = ;
}
}
int lim = route.size(), l = , r = ;
for(int i = ; i < lim; ++i) route.push_back(route[i]);
d[] = q[l] = ;
for(int i = ; i < route.size(); ++i) d[i + ] = d[i] + w[route[i]];
ll delta = mx[route[]] + d[];
for(int i = ; i < route.size(); ++i)
{
while(q[r] - q[l] + >= lim && l <= r) ++l;
if(i) delta = max(delta, mx[route[i]] + d[i] + mx[route[q[l]]] - d[q[l]]);
delta = max(delta, dp[route[i]]);
ll x = mx[route[i]] - d[i];
while(l <= r) if(x > mx[route[q[r]]] - d[q[r]]) --r; else break;
q[++r] = i;
}
ans += delta;
dfs(u);
}
int main()
{
// int size = 50 << 20; // 256MB
// char *p = (char*)malloc(size) + size;
// __asm__("movl %0, %%esp\n" :: "r"(p));
freopen("isl.in", "r", stdin);
freopen("isl.out", "w", stdout);
n = read();
for(int i = ; i <= n; ++i)
{
int u, w; u = read(); w = read();
link(i, u, w); link(u, i, w);
}
for(int i = ; i <= n; ++i) if(!used[i]) solve(i);
printf("%lld\n", ans);
fclose(stdin); fclose(stdout);
return ;
}
bzoj1791的更多相关文章
- 【bzoj1791】岛屿
[bzoj1791]岛屿 题意 求基环树的直径. \(n\leq 100000\) 分析 这道题的题解貌似很少啊. 所以自己也写一份吧. 首先找出基环树的环. 那么树的直径有两种情况: ①以环为根的某 ...
- [BZOJ1791][IOI2008]Island岛屿(环套树DP)
同NOI2013快餐店(NOI出原题?),下面代码由于BZOJ栈空间过小会RE. 大致是对每个连通块找到环,在所有内向树做一遍DP,再在环上做两遍前缀和优化的DP. #include<cstdi ...
- BZOJ1791: [Ioi2008]Island 岛屿
BZOJ1791: [Ioi2008]Island 岛屿 Description 你将要游览一个有N个岛屿的公园. 从每一个岛i出发,只建造一座桥. 桥的长度以Li表示. 公园内总共有N座桥. 尽管每 ...
- BZOJ1791 基环树直径
非递归版4S /************************************************************** Problem: 1791 User: 18357 Lan ...
- [IOI2008/BZOJ1791 岛屿](处理基环树的小技巧&基于bfs树形DP)
IOI2008/BZOJ1791 岛屿 题目大意是在一个基环树森林里求每一棵基环树的直径①的和. 其实就是树的直径的基环树升级版.我们先把环找出来,然后从环上的每一个节点x出发,并且不经过环上其他节点 ...
- [bzoj1791][ioi2008]Island 岛屿(基环树、树的直径)
[bzoj1791][ioi2008]Island 岛屿(基环树.树的直径) bzoj luogu 题意可能会很绕 一句话:基环树的直径. 求直径: 对于环上每一个点记录其向它的子树最长路径为$dp_ ...
- 【BZOJ1791】【IOI2008】【基环树】island(status第一速度)
1791: [Ioi2008]Island 岛屿 Time Limit: 20 Sec Memory Limit: 162 MB Submit: 908 Solved: 159 [Su ...
- bzoj1791: [Ioi2008]Island 岛屿 单调队列优化dp
1791: [Ioi2008]Island 岛屿 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 1826 Solved: 405[Submit][S ...
- bzoj1791[IOI2008]Island岛屿(基环树+DP)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1791 题目大意:给你一棵n条边的基环树森林,要你求出所有基环树/树的直径之和.n< ...
- BZOJ1791[Ioi2008]Island 岛屿 ——基环森林直径和+单调队列优化DP+树形DP
题目描述 你将要游览一个有N个岛屿的公园.从每一个岛i出发,只建造一座桥.桥的长度以Li表示.公园内总共有N座桥.尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走.同时,每一对这样的岛屿,都有一 ...
随机推荐
- ruby cucumber安装
创建rails工程
- history.go history.back()
转http://www.mikebai.com/Article/2009-11/757.html <input type=button value=刷新 onclick="window ...
- 杭电 1862 EXCEL排序(sort+结构体)
Description Excel可以对一组纪录按任意指定列排序.现请你编写程序实现类似功能. Input 测试输入包含若干测试用例.每个测试用例的第1行包含两个整数 N (<=100000 ...
- Java 集合相关
对整个体系做一个记录,并不涉及详细应用 Object类 1. 重写toString方法 System.out.println可以打印任何对象在于Object类拥有一个方法 public String ...
- jar项目 BeanDefinitionParsingException: Configuration problem:Unable to locate Spring NamespaceHandler for XML schema namespace
最近由于项目需要,需要jar项目来处理. 我在项目中整合了Spring,在编辑器中启动没有问题,但是使用ant打包为一个完整jar文件,部署后启动报错如下 org.springframework.be ...
- angular(转)
学习之前可以看看 知乎上讨论angularjs优缺点 帮你选择框架的网站 同类主流框架对比 教程 angularjs在慕课网 angularjs在51cto angularjs在图灵社区 社区 Ang ...
- 7-10 公路村村通(30 分)(最小生成树Prim算法)
7-10 公路村村通(30 分) 现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本. 输入格式: 输入数据包括城镇数目正整数N(≤1 ...
- markman & psd
markman & psd MarkMan 设计稿标 & 测量神器 http://www.getmarkman.com/ https://www.jianshu.com/p/83af3 ...
- CSU 1258 异或运算的线段树
题目大意:在给定区间内对每个数的最后一个二进制为1的位将其修改为0,如果数本身已经为0了,就不做改变 输出给定区间的所有数的异或值 #include <cstdio> #include & ...
- navicat mysql 连接本地 忘记密码 查看密码 操作
https://jingyan.baidu.com/article/454316ab4e9e65f7a7c03ad1.html