bzoj 4071 [Apio2015]巴邻旁之桥 splay

本文介绍了一种解决特定过桥问题的算法方案,该问题涉及到如何最小化总行走距离。通过分析不同情况下的最优解策略,并使用平衡树结构进行高效计算。

500题留念:
这里写图片描述

对于不过桥的人在最后加进答里就行了。
一个人过桥一定是先从a到桥再从桥到b。

对于K=1的情况,设桥的位置为x,答案就是|a[i]x|+|b[i]x|。因此选在所有a和b的中间最优。
对于K!=1的情况,考虑如果当前点选的桥坐标为x,那么对于这个点,答案是ba+2x(a,b都在桥左),|ba|(a,b跨桥),a+b2x(a,b都在桥右)。
因此可以发现当桥的位置为a+b2 时最优,离a+b2 越远越不优。
因此每个点选离a+b2 最近的桥。将所有点按a+b2 排序。那么一个前缀的点去第一个桥,剩下的后缀的点去第二个桥。
然后在前缀和后缀内用K=1时的方法。
用两个平衡树维护前缀和后缀。支持插入,删除,查询第K大,求子树点到定点的距离(维护区间的size和区间所有点到inf和-inf的距离和)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define which(x) (ch[fa[x]][1]==x)
const int inf=1e9+10,N=110000;
int K,n,cnt;
ll ans,fin;
char s1[11],s2[11];
struct node
{
    int x,y;
    node(){}
    node(int x,int y):x(x),y(y){}
    friend bool operator < (const node &r1,const node &r2)
    {return r1.x+r1.y<r2.x+r2.y;}
}a[N];
struct Splay
{
    int cnt,root;
    int size[N<<1],ch[N<<1][2],fa[N<<1],val[N<<1],num[N<<1];
    ll sl[N<<1],sr[N<<1];
    void pushup(int x)
    {
        sl[x]=sl[ch[x][0]]+sl[ch[x][1]]+(ll)num[x]*(inf+val[x]);
        sr[x]=sr[ch[x][0]]+sr[ch[x][1]]+(ll)num[x]*(inf-val[x]);
        size[x]=size[ch[x][0]]+size[ch[x][1]]+num[x];
    }
    Splay()
    {
        cnt=2;root=1;
        ch[1][0]=2;fa[2]=1;
        val[2]=-inf;val[1]=inf;
        num[1]=num[2]=1;
        pushup(2);pushup(1);
    }
    void rotate(int x)
    {
        int y=fa[x],k=which(x);
        ch[y][k]=ch[x][k^1];
        ch[x][k^1]=y;
        ch[fa[y]][which(y)]=x;

        fa[x]=fa[y];fa[y]=x;
        fa[ch[y][k]]=y;
        pushup(y);pushup(x);
    }
    void splay(int x,int tar)
    {
        while(fa[x]!=tar)
        {
            int y=fa[x];
            if(fa[y]==tar)rotate(x);
            else 
            {
                if(which(x)^which(y))
                    rotate(x);
                else rotate(y);
                rotate(x);
            }
        }
        if(!tar)root=x;
    }
    int find_val(int x,int v)
    {
        if(val[x]==v)return x;
        if(v<val[x])return find_val(ch[x][0],v);
        return find_val(ch[x][1],v);
    }
    int find_kth(int x,int K)
    {
        if(K>size[ch[x][0]]&&size[ch[x][0]]+num[x]>=K)
            return x;
        if(size[ch[x][0]]>=K)
            return find_kth(ch[x][0],K);
        return find_kth(ch[x][1],K-size[ch[x][0]]-num[x]);
    }
    void up(int x)
    {
        if(!x)return;
        pushup(x);up(fa[x]);
    }
    void insert(int v)
    {
        int x=root;
        while(1)
        {
            if(val[x]==v)
                {num[x]++;up(x);break;}
            if(v<val[x])
            {
                if(!ch[x][0])
                {
                    ch[x][0]=++cnt;fa[cnt]=x;
                    num[cnt]=size[cnt]=1;val[cnt]=v;
                    up(cnt);break;
                }
                x=ch[x][0];
            }
            else
            {
                if(!ch[x][1])
                {
                    ch[x][1]=++cnt;fa[cnt]=x;
                    num[cnt]=size[cnt]=1;val[cnt]=v;
                    up(cnt);break;  
                }
                x=ch[x][1];
            }
        }
        splay(x,0);
    }
    void del(int v)
    {
        int x=find_val(root,v);
        num[x]--;splay(x,0);
    }
    ll query()
    {
        int x=find_kth(root,size[root]/2);
        splay(x,0);
        return sr[ch[x][0]]-(ll)(inf-val[x])*size[ch[x][0]]
        +sl[ch[x][1]]-(ll)(val[x]+inf)*size[ch[x][1]]-
        (val[x]+inf)-(inf-val[x]);
    }
}tr1,tr2;
int main()
{
    //freopen("tt.in","r",stdin);
    scanf("%d%d",&K,&n);
    for(int i=1,x1,x2;i<=n;i++)
    {
        scanf("%s%d%s%d",&s1,&x1,&s2,&x2);
        if(s1[0]==s2[0])ans+=abs(x1-x2);
        else a[++cnt]=node(x1,x2),ans++;
    }
    sort(a+1,a+1+cnt);
    fin=1ll<<60;
    for(int i=1;i<=cnt;i++)
    {
        tr2.insert(a[i].x);
        tr2.insert(a[i].y);
    }
    fin=min(fin,tr1.query()+tr2.query());
    if(K==1)return printf("%lld\n",fin+ans),0;
    for(int i=1;i<=cnt;i++)
    {
        tr2.del(a[i].x);
        tr2.del(a[i].y);
        tr1.insert(a[i].x);
        tr1.insert(a[i].y);
        fin=min(fin,tr1.query()+tr2.query());
    }
    printf("%lld\n",ans+fin);
    return 0;
}
### NOIP2015 运输计划 BZOJ4326 题解分析 #### 问题背景 该问题是经典的图论优化问题之一,主要考察树结构上的路径操作以及高效的数据处理能力。题目要求在一个由 $n$ 个节点组成的无向连通树中找到最优的一条边将其改造为虫洞(通过此边不需要耗费时间),从而使得给定的 $m$ 条运输路径中的最长耗时最小化。 --- #### 解决方案概述 解决这一问题的核心在于利用 **二分答案** 和 **树上差分技术** 的组合来实现高效的计算过程。以下是具体的技术细节: 1. **二分答案**: 设当前目标是最小化的最大路径长度为 $T_{\text{max}}$。我们可以通过二分的方式逐步逼近最终的结果。每次尝试验证是否存在一种方式将某条边改为虫洞后使所有路径的最大值不超过当前设定的目标值 $mid$[^1]。 2. **路径标记与统计**: 使用树上差分的思想对每一条路径进行标记并快速统计受影响的情况。假设两点之间的最近公共祖先 (Lowest Common Ancestor, LCA) 是 $r = \text{lca}(u_i, v_i)$,则可以在三个位置分别施加影响:增加 $(u_i + 1), (v_i + 1)$ 同时减少 $(r - 2)$。这种操作能够有效覆盖整条路径的影响范围,并便于后续统一查询和判断[^1]。 3. **数据结构支持**: 结合线段树或者 BIT (Binary Indexed Tree),可以进一步加速区间修改和单点查询的操作效率。这些工具帮助我们在复杂度范围内完成大量路径的同时更新和检索需求[^2]。 4. **实际编码技巧**: 实现过程中需要注意一些边界条件和技术要点: - 正确维护 DFS 序列以便映射原树节点到连续编号序列; - 准备好辅助函数用于快速定位 LCA 节点及其对应关系; - 编码阶段应特别留意变量初始化顺序及循环终止逻辑以防潜在错误发生。 下面给出一段基于上述原理的具体 Python 实现代码作为参考: ```python from collections import defaultdict, deque class Solution: def __init__(self, n, edges): self.n = n self.graph = defaultdict(list) for u, v, w in edges: self.graph[u].append((v, w)) self.graph[v].append((u, w)) def preprocess(self): """Preprocess the tree to get dfs order and lca.""" pass def binary_search_answer(self, paths): low, high = 0, int(1e9) best_possible_time = high while low <= high: mid = (low + high) // 2 if self.check(mid, paths): # Check feasibility with current 'mid' best_possible_time = min(best_possible_time, mid) high = mid - 1 else: low = mid + 1 return best_possible_time def check(self, limit, paths): diff_array = [0]*(self.n+1) for path_start, path_end in paths: r = self.lca(path_start, path_end) # Apply difference on nodes based on their relationship. diff_array[path_start] += 1 diff_array[path_end] += 1 diff_array[r] -= 2 suffix_sum = [sum(diff_array[:i]) for i in range(len(diff_array)+1)] # Verify whether any edge can be modified within given constraints. possible_to_reduce_max = False for node in range(1, self.n+1): parent_node = self.parent[node] if suffix_sum[node]-suffix_sum[parent_node]>limit: continue elif not possible_to_reduce_max: possible_to_reduce_max=True return possible_to_reduce_max # Example usage of class methods would follow here... ``` --- #### 总结说明 综上所述,本题的关键突破点在于如何巧妙运用二分策略缩小搜索空间,再辅以恰当的树形结构遍历技术和差分手段提升整体性能表现。这种方法不仅适用于此类特定场景下的最优化求解任务,在更广泛的动态规划领域也有着广泛的应用前景[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值