UVALive 7261 A - Xiongnu's Land

来源:互联网 发布:1688一键传淘宝教程 编辑:程序博客网 时间:2024/05/29 07:03

Wei Qing (died 106 BC) was a military general of the Western Han dynasty whose campaigns against
the Xiongnu earned him great acclaim. He was a relative of Emperor Wu because he was the younger
half-brother of Empress Wei Zifu (Emperor Wu’s wife) and the husband of Princess Pingyang. He was
also the uncle of Huo Qubing, another notable Han general who participated in the campaigns against
the Xiongnu and exhibited outstanding military talent even as a teenager.
Defeated by Wei Qing and Huo Qubing, the Xiongnu sang: “Losing my Qilian Mountains, made
my cattle unthriving; Losing my Yanzhi Mountains, made my women lacking rouge.”
The text above is digested from Wikipedia. Since Wei and Huo’s distinguished achievements,
Emperor Wu decided to give them some awards — a piece of land taken by them from Xiongnu. This
piece of land was located in a desert, and there were many oases in it. Emperor Wu wanted to draw
a straight south-to-north dividing line to divide the land into two parts, and gave the western part to
Wei Qing while gave the eastern part to Huo Qubing. There are two rules about the land dividing:
1. The total area of the oases lay in Wei’s land must be larger or equal to the total area of the oases
lay in Huo’s land, and the difference must be as small as possible.
2. Emperor Wu wanted Wei’s land to be as large as possible without violating the rule 1.
To simplify the problem, please consider the piece of land given to Wei and Huo as a square on a
plane. The coordinate of its left bottom corner was (0, 0) and the coordinate of its right top corner
was (R, R). Each oasis in this land could also be considered as a rectangle which was parallel to the
coordinate axes. The equation of the dividing line was like x = n, and n must be an integer. If the
dividing line split an oasis, then Wei owned the western part and Huo owned the eastern part. Please
help Emperor Wu to find out how to draw the dividing line.

Input
The first line of the input is an integer K meaning that there are K (1 ≤ K ≤ 15) test cases.
For each test case:
The first line is an integer R, indicating that the land’s right top corner was at (R, R) (1 ≤ R ≤
1, 000, 000)
Then a line containing an integer N follows, indicating that there were N (0 < N ≤ 10000) oases.
Then N lines follow, each contains four integers L, T, W and H, meaning that there was an
oasis whose coordinate of the left top corner was (L, T), and its width was W and height was H.
(0 ≤ L, T ≤ R, 0 < W, H ≤ R). No oasis overlaps.
Output
For each test case, print an integer n, meaning that Emperor Wu should draw a dividing line whose
equation is x = n. Please note that, in order to satisfy the rules, Emperor might let Wei get the whole
land by drawing a line of x = R if he had to.
Sample Input
2
1000
2
1 1 2 1
5 1 2 1
1000
1
1 1 2 1
Sample Output
5
2

/* 题意:有多个矩形分布在[0, 0]到[R, R]的的范围内。     画一条竖线分割成两块矩形,使得左边包括矩形的面积大于等于右边的面积。     在这个前提下使得画的竖线尽量远. 分析:二分答案。     当面积相等时,判断再往右一个单位是否还可以相等,若不行则答案唯一确定。     否则可以往右移动。即最后的竖线要不在一个矩形内或者在矩形边缘时最优。*/#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>const int MAXN=10005;const int MAXM=1000001;int re=MAXM;using namespace std;int num;struct area{    long long w,h;    long long from;}s[MAXN];bool cmp(area a,area b){    return a.from<b.from;}long long work(long long a){    long long ans=0;    for(int i=0;i<num;i++)    {        if(s[i].from>=a)//绿洲在切线右边        {            break;        }       long long minn=min(a,s[i].from+s[i].w);        ans+=(minn-s[i].from)*s[i].h;    }    return ans;}int main (void){    int t;    cin>>t;    while(t--)    {        int top;        long long ss=0;        scanf("%d",&top);        long long l=top,r=0;        scanf("%d",&num);        for(int i=0;i<num;i++)        {            long long x,y,w,h;            scanf("%lld %lld %lld %lld",&x,&y,&w,&h);            s[i].from=x;            s[i].w=w;            s[i].h=h;            ss+=w*h;            l=min(x,l);            r=max(r,x+w);        }        sort(s,s+num,cmp);        //对绿洲从左到右排序        while(r-l>1)        //第一次二分求出左边面积大于等于右边面积时,        //此时左边面积等于多少        {            long long mid=(l+r)/2;            long long mids=work(mid);            if(mids<=ss-mids)            {                l=mid;//若左边面积比右边小            }            else            {                r=mid;            }        }        long long s1=work(l);//得到以l为切线的左边面积        long long now=l;        if(s1<ss-s1)        {            now=r;            s1=work(r);        }//左边面积比右边小的话,就将切线取r(取比较大的)        l=now;//当前切线        r=top;//总        while(r-l>1)        //对当前切线到边界的区间进行二分,得到两边差距尽可能小的两块        //让左边面积等于这么多,然后更加靠近右边的位置是哪里。        {            long long mid=(r+l)/2;            if(work(mid)<=s1)//若mid左边面积比s1小            {                l=mid;            }            else                r=mid;        }        if(work(r)>s1)//若以r为边界的左边面积比初始面积大            printf("%lld\n",l);        else            printf("%lld\n",r);    }    return 0;}

这里贴一种别人的线性扫描的方法。线性扫描好短= =

/*复杂度分析:从所给数据范围看,R在106数量级,既然n取[0,R]的整数,那么若以R为数据规模,对x=i, i:0~R进行步长为1的线性扫描,假设每次迭代中基本操作次数为常数,则渐进复杂度为O(n)。假设计算环境1000ms的时间可完成108规模的基本运算,则本题O(n)的线性扫描思路从渐进复杂度的意义上讲是可行的。确定了线性扫描的思路,接下来要考虑如何把每轮迭代代价控制在常数以及扫描停止的条件。1. 如果在每轮迭代中,都检查所有N个绿洲以求出所划分的面积,那么每轮迭代的复杂度为T(N),整体复杂度上升到了T(N*R), 即1010显然不可行。此方法的低效在于它没有为线性扫描这一“算法”设计合适的“数据结构”来存放绿洲的数据。题目输入的绿洲是一个个分散的个体,而从坐标出发的线性扫描需要快速获得以扫描位置 x=i 为自变量的左侧累加面积,这一“快速”,常数最好,至少不能和N在同一数量级。因此,要进行预处理将原始的绿洲数据转换为以横坐标为中心的统计值,以使每次迭代能用1~2个基本操作得到当前累加面积进而判断下一步的走向。2. 扫描可以从最左侧的x=0开始,不断向右移动(保证绿洲面积左侧 < 右侧)。遇到第一个理想位置(左侧>=右侧,满足了(1))后继续试探,直至抵达最理想的位置(左侧绿洲面积不增的条件下,为满足(2)尽量再往右移动)停止。由于扫描是线性的,可利用一个累加变量,每次只取当前“列”的面积作累加即可。至此,对绿洲数据的预处理结果要求已经比较明确了。即得到 x=i 代表的一段宽度为1(可以是i ~ i+1),高度为R的土地中绿洲的总面积,不妨用x[i]表示。那么这段预处理所花费的时间呢,这回要以N为数据规模来考虑,假设所有绿洲被读入结构体数组中,则对j:0~N-1进行步长为1的线性扫描,假设每次迭代中基本操作次数为常数,N在104数量级,完全可行。但处理每个绿洲真的是常数时间吗,其实应该是T(W),因为要把宽度切分为长度为1的W段,累加到x数组的W个元素上。除非x用的不是朴素的一维数组,否则整体的渐进复杂度应为O(N*W),又是1010。*/#include <cstdio>#include <cstring>using namespace std;const int MAX_R = 1000005;const int MAX_N = 10005;int K;int R, N;struct Rec{    int L, T;    long long W, H; //注意类型!!}rec[MAX_N];int x[MAX_R];int main(){    //freopen("in.txt","r",stdin);    //freopen("out.txt","w",stdout);    scanf("%d",&K);    while(K--){        scanf("%d",&R);        scanf("%d",&N);        memset(x,0,sizeof(x));        long long sum = 0;        for(int i=0; i<N; i++){            scanf("%d%d%d%d",&rec[i].L,&rec[i].T,&rec[i].W,&rec[i].H);            sum += rec[i].W*rec[i].H;            for(int j=rec[i].L; j<rec[i].L+rec[i].W; j++)                x[j] += rec[i].H;        }        long long wei = 0;        int i;        for(i=0; wei*2 < sum; i++)            wei += x[i];        while(x[i]==0 && i<R) i++;        printf("%d\n",i);    }    return 0;}
0 0