51nod1671【货物运输】

开始天真的我以为这道题和运输计划是一样的套路。于是写了一发,debug后发现过了第一个点,十分开心的交了一发,结果只过了第一个点。后来发现这个并不是一样的,因为修建黑洞之后路径法变了,而运输计划没有(树上两点间路径唯一)。于是,第一题就成了题解题...
这道题显然满足二分性质。问题在与如何判断合法。
首先,\(<=mid\) 的路径肯定满足,对于不满足的路径 \(l\)\(r\),一定是在 \(x\)\(y\) 处修建黑洞以后,\(l\) -> \(x\) -> \(y\) -> \(r\),且满足不等式:
\[\left | l-x \right | + \left | r-y \right | <= mid\]
解得:
\[y\in\left [ l+r-mid+x,r-l+mid-x \right ]\left ( l>=x \right )\]
\[y\in\left [ r-l-mid+x,r+l+mid-x \right ]\left ( l<x \right )\]
暴力的做法是枚举 \(x\),对于每一个区间求出 \(y\) 的取值范围,然后判断是否有交集。
但这样是 \(O(n^2)\) 的,需要优化。可以把区间分成两部分,前一部分 \(l<x\),后一部分 \(l>=x\),在两个部分分别求交集,再两部分的交集判断是否有交集。这样的话,前一部分的区间都有一个增量 \(x\),交集就是 \(l+r-mid\) 的交集再加上 \(x\),对于 \(l+r-mid\) 的交集可以 \(O(n)\) 预处理。后一部分同理。前一部分预处理处理前缀,后一部分后缀。
然后就可以 \(O(1)\) 判断两部分的交集是否有交集。

#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define RG register

inline int gi()
{
    RG int ret; RG bool flag; RG char ch;
    ret=0, flag=true, ch=getchar();
    while (ch < '0' || ch > '9')
        ch == '-' ? flag=false : 0, ch=getchar();
    while (ch >= '0' && ch <= '9')
        ret=(ret<<3)+(ret<<1)+ch-'0', ch=getchar();
    return flag ? ret : -ret;
}

const db pi = acos(-1.0);
const int N = 5e5+5, inf = 1<<30;

int n,m,cf[N],l1[N],r1[N];
struct range
{
    int l,r;
    inline bool operator <(const RG range &R) const { return l < R.l; }
}ra[N];
struct Range
{
    int l,r,l1,l2,r1,r2;
}g[N];

inline bool check(RG int lim)
{
    RG int i,cnt,p,l2,r2;
    cnt=0;
    for (i=1; i<=m; ++i)
        {
            if (ra[i].r-ra[i].l <= lim)
                continue;
            cnt++;
            g[cnt].l=ra[i].l, g[cnt].r=ra[i].r;
            g[cnt].l1=ra[i].l+ra[i].r-lim;
            g[cnt].l2=ra[i].r-ra[i].l-lim;
            g[cnt].r1=ra[i].r-ra[i].l+lim;
            g[cnt].r2=ra[i].l+ra[i].r+lim;
        }  //预处理
    p=cnt;
    l1[n+1]=l2=-inf, r1[n+1]=r2=inf;
    for (i=n; i; --i)
        {
            l1[i]=l1[i+1], r1[i]=r1[i+1];
            while (p && g[p].l >= i)
                {
                    l1[i] < g[p].l1 ? l1[i]=g[p].l1 : 0;
                    r1[i] > g[p].r1 ? r1[i]=g[p].r1 : 0;
                    p--;
                }
        }  //预处理对于每一个x后一部分的解集
    p=1;
    for (i=1; i<=n; ++i)
        {
            while (p <= cnt && g[p].l <= i)
                {
                    l2 < g[p].l2 ? l2=g[p].l2 : 0;
                    r2 > g[p].r2 ? r2=g[p].r2 : 0;
                    p++;
                }  //计算对于每一个x前一部分的解集
            if (l1[i] <= r1[i]+(i<<1) && l2+(i<<1) <= r2
                 && !((r1[i] < l2) || (l1[i] > r2)))
                return true;  //判断前后是否分别有解及两部分是否有交集
        }
    return false;
}

int main()
{
    freopen("trans.in","r",stdin);
    freopen("trans.out","w",stdout);
    n=gi(), m=gi();
    RG int i,l,r,mid;
    for (i=1; i<=m; ++i)
        {
            ra[i].l=gi(), ra[i].r=gi();
            if (ra[i].l > ra[i].r)
                swap(ra[i].l,ra[i].r);
        }
    sort(ra+1,ra+m+1);
    l=0, r=n+1;
    while (l <= r)
        {
            mid=(l+r)>>1;
            if (check(mid))
                r=mid-1;
            else
                l=mid+1;
        }
    printf("%d\n",r+1);
    return 0;
}

转载于:https://www.cnblogs.com/y142857/p/7525271.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值