NOI2013快餐店【图上找环+线段树】
来源:互联网 发布:java仓库管理 编辑:程序博客网 时间:2024/06/11 17:38
NOI 2013 快餐店
NOI
线段树
题目传送点
说白了,就是给个N个点N条边的图,然后求一个点(不一定是给的那N个点),到所有给定的点的路程最大值最小
=≡Σ((( つ•̀ω•́)つ我是蒟蒻,想了好久的二分和三分(最大值最小嘛)
题解
[朴素的想法]
我们可以先假设:如果给的是树而不是图呢?
树就很简单,
现在呢,给你一个图,怎么办,想这种树好做图不好做的可以把图尽量向树的方向靠拢,现在N个点,N条边,也就是说是一棵外向树(一个环,一些环的定点连着很多子树),那么我们就可以求出这个环(假设是
复杂度为:
[满分算法]
当然要先把环求出来(如果直接求图的“直径”会出问题,自己可以想个反例),依然要枚举那一条边不选,现在的问题就是怎样快速处理
因为是个环,方便写程序有时也降低复杂度,我们把环拆开(像沙子合并那道DP一样,乘个2倍,然后拉开),那么现在就是一条链,链上一些点向下连了一棵子树(自己脑补吧ㄟ(▔,▔)ㄏ,ubuntu下画个简图真是麻烦)
那么我们假设链上
那么我们
但是这还没有完,如果最长路并没有经过环呢(我们在用线段树维护的时候,维护的最小单元不是一个点,而是区间
复杂度:
讲完辣 (〃^∇^)ぇ∧∧∧っ
最后附上代码
#include <iostream>#include <cstdlib>#include <cstdio>#include <cstring>#include <queue>using namespace std;typedef long long lld;const int maxn = 1e5 + 13, maxm = maxn * 2;const lld INF = 0x7fffffffffffffffll / 2ll;int n, pos, head[maxm], used[maxn];int c[maxn], size;lld dis[maxm], s[maxm], d[maxm];int l[maxm * 2], r[maxm * 2], rs[maxm * 2], ls[maxm * 2], root;lld v1[maxm * 2], v2[maxm * 2], v[maxm * 2], maxSum;queue<int>Q;struct node { int v, w, last;}line[maxn * 2];//超级读数void read(int &a) { a = 0; bool judge = false; char c; while((c = getchar()) != EOF) { if(c == ' ' || c == '\n') { if(!judge) continue; return; } a = a * 10 + (c - '0'); judge = true; }}//链表存图void my_read(int a, int b, int c) { line[++pos] = (node) {b, c, head[a]}; head[a] = pos;}//深搜找环bool circleDfs(int pre, int u) { used[u] = true; for(int i = head[u]; i; i = line[i].last) { const int v = line[i].v; if(v == pre) continue; if(used[v]) { c[++size] = u; //s点保存前缀和 s[size] = (lld)line[i].w; used[v] = false; return true; } bool judge = circleDfs(u, v); if(judge) { c[++size] = u; s[size] = s[size - 1] + (lld)line[i].w; if(used[u]) return true; return false; } } return false;}void initForCircle() { circleDfs(1, 1); memset(used, 0, sizeof(used)); for(int i = 1; i <= size; i++) used[c[i]] = true;}//对链上每个点到树上求最长路径lld getFarthestDis(int pre, int u) { lld far = 0ll; for(int i = head[u]; i; i = line[i].last) { const int v = line[i].v; if(v == pre || used[v]) continue; far = max(far, getFarthestDis(u, v) + (lld)line[i].w); } return far;}//线段树标准模板void builtTree(int &i, int A, int B) { i = ++pos; l[i] = A; r[i] = B; // //最小单元应该是一个长度为1的区间 if(A + 1 == B) { v1[i] = d[A] - s[A]; v2[i] = d[B] + s[B]; v[i] = v1[i] + v2[i]; return; } int mid = (A + B) >> 1; builtTree(ls[i], A, mid); builtTree(rs[i], mid, B); int LS = ls[i], RS = rs[i]; v1[i] = max(v1[LS], v1[RS]); v2[i] = max(v2[LS], v2[RS]); v[i] = max(v[LS], v[RS]); v[i] = max(v[i], v1[LS] + v2[RS]);}lld Query(int i, int A, int B) { int L = l[i], R = r[i]; if(B <= L || A >= R) return 0; if(A <= L && R <= B) { lld temp; temp = max(v[i], maxSum + v2[i]); maxSum = max(v1[i], maxSum); return temp; } return max(Query(rs[i], A, B), Query(ls[i], A, B));}int tot, temp[maxn];//找直径int findDiameter(int pre, int u) { temp[++tot] = u; for(int i = head[u]; i; i = line[i].last) { const int v = line[i].v; if(v == pre || used[v]) continue; dis[v] = dis[u] + (lld)line[i].w; findDiameter(u, v); }}//求每棵子树的直径lld findLongestRoad(int x) { used[x] = false; int A; lld max1; tot = 0; dis[x] = 0; findDiameter(x, x); A = 0; max1 = 0ll; for(int i = 1; i <= tot; i++) if(max1 < dis[temp[i]]) max1 = dis[temp[i]], A = temp[i]; for(int i = 1; i <= tot; i++) dis[temp[i]] = INF; tot = 0; dis[A] = 0; findDiameter(A, A); A = 0; max1 = 0ll; for(int i = 1; i <= tot; i++) if(max1 < dis[temp[i]]) max1 = dis[temp[i]]; used[x] = true; return max1;}int main() { freopen("foodshop.in", "r", stdin);// freopen("test.in", "w", stdout); scanf("%d", &n); for(int i = 1; i <= n; i++) { int a, b, c; read(a); read(b); read(c); my_read(a, b, c); my_read(b, a, c); } initForCircle(); for(int i = 1; i <= size; i++) dis[c[i]] = getFarthestDis(c[i], c[i]); for(int i = 1; i <= size; i++) { d[i] = dis[c[i]]; d[i + size] = d[i]; s[i + size] = s[size] + s[i]; } pos = 0; builtTree(root, 1, size * 2); lld min1 = INF; for(int i = 1; i <= size; i++) { maxSum = -INF; lld temp = Query(root, i, i + size - 1); min1 = min(min1, temp); } for(int i = 1; i <= n; i++) dis[i] = INF; for(int i = 1; i <= size; i++) min1 = max(min1, findLongestRoad(c[i])); printf("%.1lf\n", (double)min1 / 2.0); return 0;}
- NOI2013快餐店【图上找环+线段树】
- [BZOJ3242][Noi2013]快餐店 && 环套树+线段树
- bzoj3242: [Noi2013]快餐店 树形dp+线段树
- [BZOJ3242][Noi2013]快餐店(树形dp+线段树)
- [线段树优化 DP] BZOJ 3242 [Noi2013]快餐店
- 【树DP+基环树】[NOI2013]快餐店
- NOI2013 快餐店
- [Noi2013]快餐店
- BZOJ3242: [Noi2013]快餐店
- bzoj3242: [Noi2013]快餐店
- 3242: [Noi2013]快餐店
- 【NOI2013T6】快餐店-环套树+树形DP+线段树
- bzoj 3242: [Noi2013]快餐店 dfs&递推
- [BZOJ3242][NOI2013]快餐店-基环树-动态规划
- bzoj3244 NOI2013树的计数 神奇脑洞题+线段树
- 【NOI2013】树的计数
- BZOJ3244[NOI2013树的计数]
- BZOJ3244: [Noi2013]树的计数
- B 站建立开源工作组:ijkplayer 等多个项目开源
- Swift- 自定义导航栏背景、标题和返回按钮属性
- Java如果提高反射效率
- Spark Job 调优
- MFC通过ADO连接 SQL 2005
- NOI2013快餐店【图上找环+线段树】
- Eclipse中安装Subversive (Subversion的对应模块)
- 从MVC和三层架构说到ssh整合开发-上
- 常见几种视频渲染模式介绍
- ORACLE 插入时间时显示'无效的月份'
- html5页面调用手机打电话功能
- 借助onSaveInstanceState方法来保存Acitivity状态.
- Erlang Port 小心换行
- Android 进程间通讯四种方式