Luogu 1399 [NOI 2013] 快餐店

2023-05-16

          • 传送门
          • 思路
          • 参考代码
          • Remarks
          • 总结

传送门
思路

发现这是一棵环套树。那首先我们会想到树上的情况。如果这是一棵树,我们自然会联想到树的直径,自然会想到对于树而言,答案为直径长度的一半。

证明
用反证法。假设直径的中点不是最佳选址,那么必然存在一条从该点出发的路径使得长度超过直径的一半。但这就说明存在一条比直径更长的路径,与直径的定义矛盾。
证毕。

回到环套树上,我们把这个环套树看成多棵树的根被串成一个环。首先,如果最终答案的快餐店建立在树中,那么最远的路径一定是所在树的直径,这个可以直接 DP。如果最终答案的快餐店建立在环上,那么从它出发到其它点一定会有一条环上的边不会被经过(因为都往近的走,那么就一定有个分界线)当然,就算快餐店在树中,也总有一条环上的边不会被经过,理由相同。

我们发现,最终答案一定能找到两个点,使得这两点到快餐店的距离相等且最远。如果只保证了最远而不保证相等,那么我们可以把快餐店向远的那个点移动,答案会变小。我们称这两个点为两个端点。

现在我们不知道答案的快餐店在哪儿,我们先假设两个端点都在同一棵树中。发现,如果事实并不是这样,那么正确答案一定比假设端点在同一棵树中的答案大(有一条更长的路)。如果我们假设两个端点在不同树中,而事实并不是这样的话,正确答案也一定比假设端点在不同树中的答案大。因此我们做两次假设,然后取最大值就好了。如果两个端点在同一棵树中,就是所有树的直径的最大值。接下来我们只考虑两个端点在不同树中的情况。

于是我们立即得到一个 O(n2) O ( n 2 ) 的算法:枚举最终答案不经过环上的哪条边,则我们剩下了一棵树,用 O(n) O ( n ) 的时间复杂度求树的直径。我们取它们的最小值,即为答案。为什么取最小值呢?因为最终答案一定有一条在环上不被经过的边,此时所有路径都是最优的。现在我们保留那条不走的边,删去另外一条环上的边,则原来一些最优的路线会绕路,答案就会变劣。因此最小值就是最后的答案。

这个做法显然不够,我们考虑数学化一点。现在我们可以直接把这个环套树抽象成:

即把所有树抽象成一条边,边权为这棵树过根结点的最长的祖先后代链。

我们现在的任务是:枚举环上被删除的边,然后求最长链,最后对它们求最小值

设树抽象成的边的边权为 d d 。那么答案就是:

max{di+dj+dist(i,j)}

其中 i i j 表示环上的两点, dist(i,j) d i s t ( i , j ) 表示 i i j 在枚举的边被删去时的距离。

考虑把环拆开,让环变成线性结构,这样我们就可以用前缀和来表示 dist d i s t 。为了方便地涵盖所有情况,我们可以把环拆成链后复制一遍放在后面,得到一条长度为 2l 2 l 的链(设环的长度为 l l ),然后依次考虑其中连续的长度为 l 的区间,这就相当于是在枚举断哪条边。

不妨设 i>j i > j ,那么我们要求的是:

max{di+dj+sisj} max { d i + d j + s i − s j }

其中 s s 表示环上距离的前缀和。

分组:

max{(di+si)+(djsj)}

我们用可重集维护前后的最大值和次大值,然后就能查询了。

但是如何保证 i>j i > j 呢?显然,如果 i>j i > j ,那么 di+dj+sisj>di+dj+sjsi d i + d j + s i − s j > d i + d j + s j − s i ,由于我们取 max max ,因此自然就保证了 i>j i > j 。这也是非法解不会最优的例子。

对于所有 l l 个查询结果,我们取一个最小值,代表假设答案经过环时的长度。再与假设答案是树的直径的长度取最大值即可。

时间复杂度 O(nlogn)

参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef int INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0; bool positive = true;
    char ch = getchar();
    while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
    if (ch == '-') { positive = false; ch = getchar(); }
    while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20]; int length = 0;
    if (x < 0) putchar('-'); else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
}

const int maxn = int(1e5) + 5;
int n;
struct Graph
{
    struct Edge
    {
        int to;
        int cost;
        int next;
    } edges[maxn * 2];
    int head[maxn];
    int i;
    Graph() : i() { memset(head, -1, sizeof(head)); }
    void addEdge(int from, int to, int cost)
    {
        edges[i].to = to;
        edges[i].cost = cost;
        edges[i].next = head[from];
        head[from] = i;
        i++;
    }
#define idx(G) idx_##G
#define wander(G, node) for (int idx(G) = G.head[node]; ~idx(G); idx(G) = G.edges[idx(G)].next)
#define DEF(G) const Graph::Edge& e = G.edges[idx(G)]; int to = e.to; int cost = e.cost
} G;

#define RunInstance(x) delete new x
struct brute
{
    bool vis[maxn];
    std::vector<int> onRing;
    int tag;

    bool DFS1(int node, int parent)
    {
        vis[node] = true;
        wander(G, node)
        {
            DEF(G);
            if (to == parent) continue;
            if (vis[to])
            {
                tag = to;
                onRing.push_back(idx(G) >> 1);
                return true;
            }
            if (DFS1(to, node) && tag != to)
            {
                onRing.push_back(idx(G) >> 1);
                return true;
            }
        }
        return false;
    }

    LL d;
    LL f[maxn];
    void DFS2(int node, int parent)
    {
        LL major = 0, minor = 0;
        wander(G, node)
        {
            if ((idx(G) >> 1) == tag) continue;
            DEF(G);
            if (to == parent) continue;
            DFS2(to, node);
            f[node] = std::max(f[node], f[to] + cost);
            if (f[to] + cost > minor)
                if (f[to] + cost > major)
                {
                    minor = major;
                    major = f[to] + cost;
                }
                else
                    minor = f[to] + cost;
        }
        d = std::max(d, major + minor);
    }

    brute() : vis()
    {
        DFS1(1, 0);

        LL ans = LLONG_MAX;
        for (int i = 0; i < onRing.size(); i++)
        {
            tag = onRing[i];
            memset(f, 0, sizeof(LL) * (n + 1));
            d = 0;
            DFS2(1, 0);
            ans = std::min(ans, d);
        }
        printf("%.1f", (double)ans / 2);
    }
};
LL f[maxn];
int vs[maxn * 2];
LL cost[maxn * 2];
struct work
{
    bool vis[maxn];
    std::vector<int> onRing;
    std::vector<int> vertex;
    std::vector<int> dis;
    int tag;

    bool DFS1(int node, int parent)
    {
        vis[node] = true;
        wander(G, node)
        {
            DEF(G);
            if (to == parent) continue;
            if (vis[to])
            {
                tag = to;
                onRing.push_back(idx(G) >> 1);
                vertex.push_back(to);
                dis.push_back(cost);
                return true;
            }
            if (DFS1(to, node) && tag != to)
            {
                onRing.push_back(idx(G) >> 1);
                vertex.push_back(to);
                dis.push_back(cost);
                return true;
            }
        }
        return false;
    }

    LL D[maxn];
    void DFS2(int node, int parent)
    {
        LL major = 0, minor = 0;
        D[node] = 0;
        f[node] = 0;
        wander(G, node)
        {
            DEF(G);
            if (vis[to] || to == parent) continue;
            DFS2(to, node);
            f[node] = std::max(f[node], f[to] + cost);
            D[node] = std::max(D[node], D[to]);

            if (f[to] + cost > minor)
                if (f[to] + cost > major)
                {
                    minor = major;
                    major = f[to] + cost;
                }
                else
                    minor = f[to] + cost;
            D[node] = std::max(D[node], major + minor);
        }
    }

    int l;

    struct Comp1
    {
        bool operator()(const int& a, const int& b) const
        {
            return f[vs[a]] + cost[a] > f[vs[b]] + cost[b];
        }
    };
    struct Comp2
    {
        bool operator()(const int& a, const int& b) const
        {
            return f[vs[a]] - cost[a] > f[vs[b]] - cost[b];
        }
    };

    work() : vis()
    {
        DFS1(1, 0);
        memset(vis, 0, sizeof(vis));
        for (int i = 0; i < vertex.size(); i++)
            vis[vertex[i]] = true;
        for (int i = 0; i < vertex.size(); i++)
        {
            int v = vertex[i];
            DFS2(v, 0);
        }
        l = vertex.size();
        for (int i = 0; i < l; i++)
        {
            vs[i + 1] = vertex[i];
            cost[i + 2] = dis[i];
        }
        for (int i = l + 1; i <= (l << 1); i++)
        {
            vs[i] = vs[i - l];
            cost[i + 1] = cost[i + 1 - l];
        }

        for (int i = 2; i <= (l << 1) + 1; i++)
            cost[i] += cost[i - 1];
        std::multiset<int, Comp1> set1;
        std::multiset<int, Comp2> set2;

        for (int i = 1; i <= l; i++)
        {
            set1.insert(i);
            set2.insert(i);
        }
        LL ans = LLONG_MAX;
        for (int i = l + 1; i <= (l << 1); i++)
        {
            auto it11 = set1.begin();
            auto it12 = it11; it12++;
            auto it21 = set2.begin();
            auto it22 = it21; it22++;
            if (*it11 != *it21)
                ans = std::min(ans, f[vs[*it11]] + f[vs[*it21]] + cost[*it11] - cost[*it21]);
            else
            {
                ans = std::min(ans,
                    std::max(f[vs[*it12]] + f[vs[*it21]] + cost[*it12] - cost[*it21],
                        f[vs[*it11]] + f[vs[*it22]] + cost[*it11] - cost[*it22]));
            }

            set1.erase(set1.find(i - l)); // note: find!!!
            set2.erase(set2.find(i - l));
            set1.insert(i);
            set2.insert(i);
        }

        for (int i = 0; i < vertex.size(); i++)
            ans = std::max(ans, D[vertex[i]]);
        printf("%.1f", (double)ans / 2);
    }
};

void run()
{
    n = readIn();
    for (int i = 1; i <= n; i++)
    {
        int from = readIn();
        int to = readIn();
        int cost = readIn();
        G.addEdge(from, to, cost);
        G.addEdge(to, from, cost);
    }

    RunInstance(work);
}

int main()
{
    run();
    return 0;
}
Remarks

我们要取一个滑动窗口内的最大值,这个可以用单调队列,时间复杂度为 O(n) O ( n ) 。(没试过,不过不是还要求次大值吗???)

总结

为什么我卡了一天?因为我没有注意到最重要的一点是最终答案一定有一条在环上的边不会经过。唉,我太弱啦!没学上啦!滚粗啦!唉,我太弱啦!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Luogu 1399 [NOI 2013] 快餐店 的相关文章

  • Luogu 1552 [APIO 2012] 派遣

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 题读错了两次 xff0c 一开始读成了一个一般的背包 xff0c 然后读成了一个价值和花费相同的背包 xff0c 最后才发现原来是一个价值为 1
  • 贪玩 CF 之旅

    文章目录 CF 7D Palindrome Degree http codeforces com problemset problem 7 D 题解 CF 713C Sonya and Problem Wihtout a Legend ht
  • Luogu 3638 [APIO 2013] 机器人

    传送门思路正解参考代码关于 SPFA 传送门 思路 n n 这么小 会不会是搜索题 稍有经验的我直接否定了这个结论 仔细读题并分析样例 发现原来一个位置可以有多个机器人 且机器人行走的时候无视其它机器人 那这个就是一张图啊 可以将这张图预处
  • Luogu 3647 [APIO 2014] 连珠线

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 又看错题了 题目中说一个新的珠子和一个已经添加的珠子连接起来 xff0c 我没有看到 xff0c 然后就凉了 立个 flag xff1a 已经连续看错五题了 xff0c
  • 【转】mingw64的安装方法

    转自 xff1a http write blog csdn net postlist mingw64的安装方法 1 下载ming w64 http sourceforge net projects mingw w64 files or x8
  • Luogu 3645 [APIO 2015] 雅加达的摩天楼

    传送门思路正解参考代码Update 传送门 思路 唉 xff0c 我太弱了 xff0c 我都看出来要分块了 xff0c 就是做不来 不过终于把题读对了 先来看子任务三怎么做 显然可以有一个 O m 2 O m 2
  • Luogu 3644 [APIO 2015] 八邻旁之桥

    传送门思路当 k 61 2 时参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 题也做不来 很明显这道题先要把不过河的人排除了 xff0c 剩下的都是要过河的 当 k 61 1 k 61 1 时 xff0
  • Luogu 3646 [APIO 2015] 巴厘岛的雕塑

    传送门总结 APIO 2015思路参考代码总结 传送门 总结 APIO 2015 争取今天做完一套 QAQ T1 我最多之能想到从高位向低位做 xff0c 然后就完全不会了 xff1b T2 我想到了分情况讨论 xff0c 但是没有建图成功
  • UOJ 2016 [APIO 2016] Gap

    传送门思路参考代码交互题 交互题大致形式Windows 平台下 xff08 Dev C 43 43 xff09 Ubuntu 平台下 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 题也做不来 这道题简直就是利用
  • CF 940F Machine Learning

    传送门题目大意思路参考代码Remarks 传送门 题目大意 给你一个数组 a 1 n n 10 5 a 1 n
  • CF 976D Degree Set

    传送门题目大意思路参考代码总结 传送门 题目大意 给你一个长度为 n n 的正整数序列 d 1 d 2 d n d1 d2 dn xff08 d 1 lt d 2 lt lt d n
  • Luogu 3778 [APIO 2017] 商旅

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 看到这道题就想到了二分答案找负环 xff0c 但是怎么做呢 xff1f 完全不会 唉 xff0c 我太弱啦 xff01 先注意题目中说可以重复经过点和边 x
  • CF 963E Circles of Waiting

    传送门题目大意思路参考代码 传送门 题目大意 在平面直角坐标系上 xff0c 有一个神奇的点 xff0c 一开始在 0 0 0 0 每秒钟这个点都会随机移动 xff1a 如果它在 x y
  • CF 976F Minimal k-covering

    传送门题目大意 输入格式输出格式 思路参考代码 传送门 题目大意 给你一张二分图 G 61 U V E G 61 U V
  • CF 963A Alternating Sum

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 好不容易做得来一道题 xff0c 还是 A 题 xff08 所以不要瞧不起 A 题 xff09 xff0c 结果还写错了 xff08 不知道为什
  • iOS中瀑布流布局详解

    前段时间在逛淘宝的时候发现淘宝的商品界面的布局是瀑布流 我记得明明之前不是瀑布流的 x1f611 刚好手上活忙完了 xff0c 写了一个瀑布流的布局 xff0c 简单的封装了下 xff0c 以便日后使用 x1f60f 其实说到底瀑布流也就是
  • Luogu 2146 [NOI 2015] 软件包管理器

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 好不容易遇到一道傻逼题 xff0c 又出了个傻逼错误 xff0c 爆得只剩 30 30 分了 唉 xff0c 我太弱啦 xff01 显然 xff
  • Luogu 2150 [NOI 2015] 寿司晚宴

    传送门思路对于 30 30 30 的数据对于 100 100 100 的数据参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 完全做不来 xff0c 连暴力都打不来 主要好像是因为我从来没有做过以质因子为
  • Luogu 3649 [APIO 2014] 回文串

    传送门思路Manacher 算法 特殊字符回文半径算法与实现本质不同的回文串个数 正解参考代码总结 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 这道题各路神仙都说是模板题 xff0c 但我觉得完全不可做 xf
  • Luogu 2168 [NOI 2015] 荷马史诗

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连哈夫曼树都不会 这道题就是一个 k k 叉哈夫曼树 题目要求满足两个条件 一是代价最小 二是最长长度最小 最长长度最小很好解决 只需要优先合并

随机推荐

  • Luogu 2178 [NOI 2015] 品酒大会

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 做了两个星期的题 xff0c 自己做出来的才只有这一道 xff0c 唉 xff0c 我太弱啦 xff01 我们考虑第一问怎么做 题目中相似的概念
  • Luogu 1117 [NOI 2016] 优秀的拆分

    传送门思路利用后缀数组解决重复子串问题注意事项参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连暴力都想不到 xff0c 唉 xff0c 我太弱啦 xff01 考虑暴力法 xff0c 可以枚举一个中间点
  • Luogu 1712 [NOI 2016] 区间

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 这么个傻逼题 xff0c 居然把离散化写错了 xff0c 唉 xff0c 我太弱啦 xff01 显然我们可以考虑枚举最短长度和最长长度 xff0
  • CF 977F Consecutive Subsequence

    传送门思路参考代码 传送门 思路 CF 的第一场 div3 xff0c 在我提交了一份有错的代码后突然不能提交了 xff0c 在跑什么 System Testing xff0c 我就跟它杠上了 xff0c 直到它评测完 唉 xff0c 我太
  • CF 7D Palindrome Degree

    传送门思路参考代码 传送门 思路 不是马拉车加随便 DP 乱搞 xff1f 本来想复习一下马拉车的 xff0c 结果拉出了许多事端 xff08 修复了 OI Learner Judge 的严重 bug 一个害我调了两节课的 bug xff0
  • Luogu 3822 [NOI 2017] 整数

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 当年网同这道题还拿了 16 16 分 xff0c 现在一分都不会做了 xff0c 唉 xff0c 我太弱啦 xff01 这道题其实是很不错的 x
  • 【Go】go语言中切片的长度变化后容量的变化

    一 新增信息长度 43 当前长度 lt 61 当前容量 span class token keyword func span span class token function printSlice span span class toke
  • APIO 2018 Practice Session T1 Wedding cake

    没有传送门题目大意思路参考代码熟悉环境 没有传送门 题目大意 给你一个长度为 n n 的正整数序列 a i ai xff0c 要求构造出 n n 个小数 使得它们的和为 1 1 xff0c 且每个数小数点后恰好有
  • APIO 2018 Practice Session T3 / CF 936C Lock Puzzle

    传送门题目大意思路参考代码总结 传送门 题目大意 给你一个字符串 origin xff0c 一个字符串 target xff0c 长度均为 n n 要求在 3 n 3n xff08 5 2 n 5 2
  • APIO 2018 游记

    Day 0Day 1Day 2Day 3Day 4 Day 0 早上 4 4 点就上车去机场赶那 7 7 点的飞机 感觉很困 xff0c 所以在飞机上就这么睡过去了 北京是个好地方 xff0c 但是与我无关 下飞机后 xff0c 我们一行人
  • Luogu 2375 [NOI 2014] 动物园

    文章目录 传送门思路参考代码Review 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 连 KMP 也不会 xff0c WA 飞了 xff0c 唉 xff0c 我太弱啦 xff01 首先 xff0c 原始的 K
  • Luogu 2114 [NOI 2014] 起床困难综合症

    传送门思路参考代码 传送门 思路 按位贪心 但是我太弱了 xff0c 明明可以 O n O n 预处理 xff0c 我却只会 O 32 n O
  • Luogu 2354 [NOI 2014] 随机数生成器

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 这么一个傻逼题 xff0c 我却看成了要你构造一种交换方案使得答案最小 xff0c 结果后面的额外交换是题目给定的 唉 xff0c 我太弱啦 x
  • Luogu 2305 [NOI 2014] 购票

    传送门思路别人家的题解弱化的传送门 xff08 Luogu 3994 高速公路 xff09 参考代码 对于没有距离限制的 50 分 参考代码 对于 100 分的数据参考代码Remarks 传送门 思路 唉 xff0c 我太弱了 xff0c
  • Luogu 1224 [NOI 2013] 向量内积

    传送门思路参考代码 传送门 思路 唉 xff0c 我太弱了 xff0c 什么都不会 xff0c 听都没有听说过这类题 xff0c 唉 xff0c 我太弱啦 xff01 显然这道题可以在 O n 2 d O n 2
  • Luogu 1397 [NOI 2013] 矩阵游戏

    传送门思路正解参考代码Remarks 传送门 思路 唉 xff0c 我太弱了 xff0c T1 都做不来 xff0c 唉 xff0c 我太弱啦 xff01 显然这个题可以用矩阵快速幂在 O log n O log n
  • Luogu 2414 [NOI 2011] 阿狸的打字机

    文章目录 传送门思路参考代码总结 传送门 思路 首先我们甚至不能单独保存每个字符串 xff0c 因为总长度可以达到 O n 2
  • kali新手入门教学(10)--ping的讲解

    Ping 是 Windows 和 Linux 都自带的一个扫描工具 xff0c 用于校验与远程计算机或本机的连接 只有在安装 TCP IP 协议之后才能使用该命令 Ping 命令通过向计算机发送 ICMP 回应 报文并且监听回应报文的返回
  • Luogu 3628 [APIO 2010] 特别行动队

    传送门思路参考代码 传送门 BZOJ 思路 设 f i f i 表示将 1 i 1 i 的士兵编
  • Luogu 1399 [NOI 2013] 快餐店

    传送门思路参考代码Remarks总结 传送门 思路 发现这是一棵环套树 那首先我们会想到树上的情况 如果这是一棵树 xff0c 我们自然会联想到树的直径 xff0c 自然会想到对于树而言 xff0c 答案为直径长度的一半 证明 用反证法 假