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;
}
posted @ 2017-09-15 11:00  tbhkoymiads  阅读(142)  评论(0编辑  收藏  举报