BZOJ1758 [WC2010]重建计划

58 篇文章 0 订阅

Address


Solution

  • 容易想到二分答案 mid m i d ,将树上每条边减去 mid m i d ,原问题被转化为求树上是否存在一条价值和大于等于 0 0 的路径,可以用点分治来做。
  • 因为题目对路径长度有限制,记录之前处理的子树中某一长度的路径价值和的最大值。
  • 对重心的每一棵子树 BFS,则按照 BFS B F S 序枚举点,路径的长度递增。
  • 枚举之前处理的子树中的路径长度,则对于当前子树可以用单调队列(滑动窗口)来维护对应的最大值,每次判断路径总价值和是否大于等于 0 0 即可。
  • 时间复杂度 O(nlogn×)
  • 尽管洛谷跑得很慢,BZOJ那么卡常竟然过了……
  • 如果有什么好的提高运行速度的方法欢迎指出。

Code

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;

const double eps = 1e-6;
const int Maxn = 0x3f3f3f3f;
const int N = 1e5 + 5, M = N << 1;
int n, L, U, Gs, Gt, tis, last, top, num;
int sze[N], len[N], G[N], h[N], ph[N]; 
bool flag, vis[N], stp[N];
double l = 1e9, r = -1e9, Ans, dis[N], f[N];

struct Edge
{
    int to; double cst; Edge *nxt;
}p[M], *lst[N], *P = p;

inline void Link(int x, int y, int z)
{
    (++P)->nxt = lst[x]; lst[x] = P; P->to = y; P->cst = z;
    (++P)->nxt = lst[y]; lst[y] = P; P->to = x; P->cst = z; 
    if (z < l) l = z; if (z > r) r = z; 
}

inline int get()
{
    char ch; int res = 0; bool flag = false;
    while (ch = getchar(), !isdigit(ch) && ch != '-');
    (ch == '-' ? flag = true : res = ch ^ 48);
    while (ch = getchar(), isdigit(ch))
        res = res * 10 + ch - 48;
    return flag ? -res : res;
}

template <class T> inline void CkMax(T &x, T y) {if (x < y) x = y;}
inline int Min(int x, int y) {return x < y ? x : y;}

inline void Dfs1(int x, int fa)
{
    int cnt = 0; sze[x] = 1; 
    for (Edge *e = lst[x]; e; e = e->nxt)
    {
        int y = e->to;
        if (y == fa || vis[y]) continue;
        Dfs1(y, x);
        sze[x] += sze[y];
        CkMax(cnt, sze[y]); 
    }
    CkMax(cnt, tis - sze[x]);
    if (cnt < Gs)
        Gs = cnt, Gt = x;
}

inline int findG(int x)
{
    Gs = Maxn;
    Dfs1(x, 0);
    return Gt;
}

inline void Init(int x)
{
    int y; G[++num] = findG(x); vis[G[num]] = true;
    for (Edge *e = lst[G[num]]; e; e = e->nxt)
        if (!vis[y = e->to]) tis = sze[y], Init(y);
}

inline void Bfs(int src)
{
    stp[h[++top] = src] = true; int x, y;
    for (int i = last + 1; i <= top; ++i)
    {
        x = h[i];
        for (Edge *e = lst[x]; e; e = e->nxt)
            if (!vis[y = e->to] && !stp[y]) 
            {
                dis[y] = dis[x] + e->cst;
                len[y] = len[x] + 1;
                stp[h[++top] = y] = true;
            }
    }
    for (int i = last + 1; i <= top; ++i) 
        stp[h[i]] = false;
}

inline void Check()
{
    int t = 1, w = 0, tip = last + 1;
    for (int i = Min(U, len[h[top]]); i >= 0; --i)
    {
        int tl = i >= L ? 0 : L - i, tr = U - i;
        while (t <= w && len[ph[t]] < tl) ++t;
        while (tip <= top && len[h[tip]] < tl) ++tip;
        while (tip <= top && len[h[tip]] <= tr)
        {
            while (t <= w && dis[ph[w]] + eps <= dis[h[tip]]) --w;
            ph[++w] = h[tip++];
        }
        if (t <= w && f[i] + dis[ph[t]] >= -eps) 
            return (void)(flag = true);
    }
}

inline void solve(int x)
{
    int Gz = G[++num], y;
    vis[Gz] = true; h[0] = Gz; 
    f[0] = dis[Gz] = len[Gz] = 0; top = 0;
    for (Edge *e = lst[Gz]; e; e = e->nxt)
        if (!vis[y = e->to])
        {
            dis[y] = e->cst; len[y] = 1; 
            last = top; Bfs(y); Check();
            for (int i = last + 1; i <= top; ++i)   
                CkMax(f[len[h[i]]], dis[h[i]]);
        }
    for (int i = 0; i <= top; ++i) 
        f[len[h[i]]] = -1e9; 
    for (Edge *e = lst[Gz]; e; e = e->nxt)
        if (!vis[y = e->to]) solve(y); 
}

inline bool Judge(double mid)
{
    memset(vis, false, sizeof(vis));
    for (int i = 1; i <= n; ++i)
        for (Edge *e = lst[i]; e; e = e->nxt)
            e->cst -= mid;
    flag = false; num = 0; solve(1); 
    for (int i = 1; i <= n; ++i)
        for (Edge *e = lst[i]; e; e = e->nxt)
            e->cst += mid;
    return flag; 
}

int main()
{
    tis = n = get(); L = get(); U = get(); int x, y;
    for (int i = 1; i < n; ++i)
    {
        x = get(); y = get();
        Link(x, y, get()); f[i] = -1e9; 
    }
    Init(1);
    for (int i = 1; i <= 32; ++i)
    {
        double mid = (l + r) / 2.0;
        if (Judge(mid)) Ans = mid, l = mid;
            else r = mid;
    }
    printf("%.3lf", Ans);
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值