您的位置:首页 > 其它

(解题报告) Uva 1616 Caravan Robbers (商队抢劫者)(上凸包+队列+结构体)

2016-01-24 13:03 405 查看
Long long ago in a far far away land there were two great cities and The Great Caravan Road between them. Many robber gangs \worked" on that road.

By an old custom the i-th band robbed all merchants that dared to travel between ai and bi miles of The Great Caravan Road. The custom was old, but a clever one, as there were no two distinct i and j such that ai aj and bj bi. Still when intervals controlled
by two gangs intersected, bloody ghts erupted occasionally. Gang leaders decided to end those wars. They decided to assign each gang a new interval such that all new intervals do not intersect (to avoid bloodshed), for each gang their new interval is subinterval
of the old one (to respect the old custom), and all new intervals are of equal length (to keep things fair).

You are hired to compute the maximal possible length of an interval that each gang would control after redistribution   

我就简单翻译下。。

很久很久以前,有两个城市,中间有一条路。这条路被强盗团伙men割据了。

根据传统,第i个强盗团会抢劫每个敢于经过ai与bi之间的所有商人。

还有一个(聪明的)传统那就是没有区间会相互包含。

当地盘交叉时就会有流血冲突。

你的任务是把所有区间变成等长的子区间,使它们之间两两不相交。

Input

The input will contain several test cases, each of them as described below.

The rst line contains n (1 n 100000) | the number of gangs. Each of the next n lines contains

information about one of the gangs | two integer numbers ai and bi (0 ai < bi 1000000). Data

provided in the input le conforms to the conditions laid out in the problem statement.

Output

For each test case, write to the output on a line by itself.

Output the maximal possible length of an interval in miles as an irreducible fraction p=q.

Note for the sample:

In the above example, one possible set of new intervals that each gang would control after redistri-

bution is given below.

The rst gang would control an interval between 7/2 = 3.5 and 12/2 = 6 miles which has length

of 5/2 and is a subinterval of its original (2, 6).

The second gang would control an interval between 2/2 = 1 and 7/2 = 3.5 miles which has length

of 5/2 and is a subinterval of its original (1, 4).

The third gang would control an interval between 16/2 = 8 and 21/2 = 10.5 miles which has

length of 5/2 and is a subinterval of its original (8, 12).

Sample Input

3

2 6

1 4

8 12

Sample Output

5/2

这看上去似乎比暴力更优雅一点。。

而且这效率也不是一般的高。。。

题解:

我们让大区间按照左端点排序, 这时右端点也一定排好序了。

         interval【i】:区间i,有左端点l,右端点r。

一条路根据强盗团伙割据的情况,会出现一个以上的块,这些块不会互相影响,这时可以分别考虑,再取其中的最小值。

考虑这样一个块,最大的子区间长度是块的总长度除以区间的个数。但是这不一定满足题意-----第i个子区间区间包含于第i个地盘。

设ans是当前状态的最优子区间长度(最长又满足题意),显然整个过程中ans是递减的。

再设now是当前已匹配的子区间的右端点, 因为我们要选尽量靠左的子区间, 保证不可能再向左移动了。所以所匹配的子区间可能是连续的一块分成好多个子区间, 这些子区间分别属于相应的大区间。

这时考虑下一个区间,如果这个区间的左端点大于now,就可以把now更新为这个点,并且ans保持不变------因为ans是递减的,并且一个符合条件的情况下再把子区间长度变小,依然是符合情况的。否则就将now延伸ans,如果延伸后仍然小于区间的右端点,嗯, ans 不变。

否则如果延伸后超过了右端点, 那么子区间长度就必须减小,,,,,

考虑这个连续的块,起点为base, 当前区间为j, 则

定义 base<=k <=i是使(interval[j].right-interval[k].left)/(j-k+1) 最小的k。

那么 max_ans = (interval[j].right-interval[k].left)/(j-k+1); 并且起点更新为k,now更新为interval[j].right.

证:

如果不是最大的ans,因为只能尽量向右靠, 所以会超过起点的左端点。

如果某个子区间不包含于大区间,设这个区间为第i个。

因为之前的加入原则,所以第j个子区间的右端点一定小于第j个大区间的右端点。

所以i_th子区间左端点小于i_th大区间左端点,这时(interval[j].right-interval[i].left)/ (j-i+1) 比定义的最小值更小,这与k的定义矛盾。

所以可以用一个上凸包来维护base到当前i的点: (j, interval[j].left); 因为ith区间本身也有可能限制缩小,所以要先加入(j. interval[j].left)------当然是从右向左更新凸包。

然后因为更新新的起点后起点之前的点就不用考虑了, 所以从前往后查找, 边查找边删除起点。 直到为空或使 (interval[j].right-interval[base].left)/(j-base+1) 最小。

这个上凸包需要用队列来维护,还有值得注意的是当前状态——总是最优的,加入一个区间后通过调整使当前状态仍然最优。

注意当now与interval【j】没有交集时要重建凸包, 于是 扫描到后半部分时,因为ans是非递增的, 所以后部会很快。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#define III 5000000
using namespace std;
typedef unsigned long long Unt;
const int Maxn = 100000 + 20;
Unt gcd(Unt a,Unt b) {
if(b==0) return a;
else return gcd(b,a%b);
}
struct fct {
Unt nume, deno;
fct(Unt x,Unt y = 1): nume(x),deno(y) {}
fct operator + (const fct& Q) {
Unt I = nume*Q.deno+deno*Q.nume, J = deno*Q.deno;
Unt j = gcd(I, J);
return fct(I/j,J/j);
}
fct operator + (const Unt& P) {
Unt I = nume + P*deno;
return fct(I,deno);
}
bool operator <= (const Unt& a) {
return nume <= deno*a;
}
bool operator < (const Unt& a) {
return nume < deno*a;
}
bool operator < (const fct& b) {
return nume*b.deno < b.nume*deno;
}
bool operator > (const fct& b) {
return nume*b.deno > b.nume*deno;
}

}ans(0),now(0);
ostream& operator << (ostream& out,const fct& d) {
out << d.nume << '/' << d.deno ;
return out;
}
struct node {
Unt l,r;
bool operator < (const node& c) {
return l < c.l;
}
}interval[Maxn];
int N,R,F;
Unt queue[Maxn];

int main()
{
while(scanf("%d",&N) != EOF) {
for(int i = 1; i <= N; i++) scanf("%d%d",&interval[i].l,&interval[i].r);
sort(interval+1,interval+1+N);
F = R = 0; ans = fct(III);
for(int i = 1; i <= N; i++) {
if(!R) {
if(!(ans<=(interval[i].r-interval[i].l))) ans=fct(interval[i].r-interval[i].l);
queue[R++] = i;
now = ans+interval[i].l;
continue;
}
if(now <= interval[i].l) {
F = R = 0; --i;continue;
}
while(R-F>1 && (i-queue[R-1])*(interval[queue[R-1]].l-interval[queue[R-2]].l) <=
(queue[R-1]-queue[R-2])*(interval[i].l-interval[queue[R-1]].l))
R--; queue[R++] = i;
if(now + ans <= interval[i].r) {
now = now + ans;
}else {
while(R-F>1 &&
(queue[F+1]-queue[F]) * (interval[i].r-interval[queue[F+1]].l) <=
(i-queue
a02e
[F+1]+1) * (interval[queue[F+1]].l-interval[queue[F]].l) )
F++;
Unt lin = interval[i].r - interval[queue[F]].l, kin = i - queue[F] + 1;
Unt uin = gcd(lin,kin);
ans = fct(lin/uin,kin/uin);
now = fct(interval[i].r);
}
}
cout << ans << endl;

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息