NOI
线段树
题目传送点
说白了,就是给个N个点N条边的图,然后求一个点(不一定是给的那N个点),到所有给定的点的路程最大值最小
=≡Σ((( つ•̀ω•́)つ我是蒟蒻,想了好久的二分和三分(最大值最小嘛)
我们可以先假设:如果给的是树而不是图呢?
树就很简单, O(n) 求出树的直径(不会点这里),因为要求最大值最小嘛,非常显然,直径的一半就是答案。
现在呢,给你一个图,怎么办,想这种树好做图不好做的可以把图尽量向树的方向靠拢,现在N个点,N条边,也就是说是一棵外向树(一个环,一些环的定点连着很多子树),那么我们就可以求出这个环(假设是 K 条边的环),然后枚举这个环上哪一条边不走,然后再做 K 次找树的直径即可。
复杂度为: O(N2) ,预计得分60
当然要先把环求出来(如果直接求图的“直径”会出问题,自己可以想个反例),依然要枚举那一条边不选,现在的问题就是怎样快速处理
因为是个环,方便写程序有时也降低复杂度,我们把环拆开(像沙子合并那道DP一样,乘个2倍,然后拉开),那么现在就是一条链,链上一些点向下连了一棵子树(自己脑补吧ㄟ(▔,▔)ㄏ,ubuntu下画个简图真是麻烦)
那么我们假设链上 K 个点,分别为 1,2,3…… ,然后我们预处理出从链上这个点到子树的最长的路径记为 Di ,链上每两个点之间的距离为 disij ,根据两点间的距离求出每个点到第一个点的距离前缀和记做 Si ,那么第 i 个点的子树到第 j 的点的子树的最大距离为 Sj−Sj+Di+Dj ,我们再移动几项: Di−Si+Dj+Sj ,所以用线段树维护一个[L, R]中 Di−Si+Dj+Sj 的最大值,用 O(log2N) 的时间得到答案。
那么我们 N 次枚举, ANS=min(Query(i,i+K−1))(i∈[1,K]) 。
但是这还没有完,如果最长路并没有经过环呢(我们在用线段树维护的时候,维护的最小单元不是一个点,而是区间 [i,i+1] ),所以,我们再和链上 K 个点的子树中的直径求 min 即可(想想为什么不是 max )。
复杂度: O(Nlog2N) ,预计得分100
讲完辣 (〃^∇^)ぇ∧∧∧っ
#include
#include
#include
#include
#include
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;
}