问题描述
想必同学们已经深刻体会到军训的辛苦了,与此同时我们也应该感谢教官们的辛苦付出。现在请你帮助教官小张解决如下问题吧!
马上集合了,但现在同学们分布在操场各个位置。共有 n 位同学,他们在操场的位置可以用
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi) 表示。现在小张想知道对于每一个 i ,前 i 个同学集合应该如何选择位置使得所有同学走的总曼哈顿距离最短。
输入
本题为多组数据。
第一行一个数, T 表示数据组数。
每组数据第一行一个数 n ( 1 ≤ n ≤ 500000 ) n(1 \leq n \leq 500000 ) n(1≤n≤500000) ,表示同学个数。
接下来 n 行,每行两个数 x i , y i ( − 1 0 9 ≤ x i , y i ≤ 1 0 9 ) x_i,y_i(-10^9 \leq x_i,y_i \leq 10^9) xi,yi(−109≤xi,yi≤109),表示每个同学位置。
数据保证所有组数据 ∑ n ≤ 500000 \sum n \leq 500000 ∑n≤500000。
输出
每组数据第一行输出数据组数的信息(见样例)。
之后 n 行,每行一个数,保留4位小数,表示前 i 位同学集合到同一个点最少走的曼哈顿距离。
注
( x 1 , y 1 ) , ( x 2 , y 2 ) 两点曼哈顿距离为 ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ (x_1,y_1),(x_2,y_2) 两点曼哈顿距离为|x_1-x_2|+|y_1-y_2| (x1,y1),(x2,y2)两点曼哈顿距离为∣x1−x2∣+∣y1−y2∣
样例
输入(1)
1
5
1 1
1 1
1 -1
-1 1
-1 -1
输出(1)
Case: 1
0.0000
0.0000
2.0000
4.0000
8.0000
代码
这个题。。。。。。怎么说呢。。。。一言难尽。。。代码虽然看着很长,其实很多部分都是重复的。当时觉得这是什么东西,堆不堆的,又上滤又下滤的是不是吃多了。。。但是现在看来,堆其实还是一种蛮重要的数据结构 ,之后数据结构这门课也会重点去讲,还是自己写写试试吧
说白了这个题的思路就是一个动态维护中位数,再说明白一点就是一定要在中位数坐标的位置的人那里集合
#include<stdio.h>//啊啊啊啊啊啊啊终于过了,我本来都打算放弃这两分了呜呜呜
long long int p;
long long int n,m,x,y,max_x[250010]={},min_x[250010]={},max_y[250010]={},min_y[250010]={};//用数组来建立堆,两个大根堆,两个小根堆
long long int left_size_x,right_size_x,left_size_y,right_size_y,sum_max_x,sum_min_x,sum_max_y,sum_min_y;//各种需要的参数
long long int middle_x,middle_y;
void shiftdownmax_x(long long int x)
{//x大根堆向下调整,不断地和自己的子节点来比较
long long int t,flag=0;
while(x*2<=left_size_x&&flag==0)
{
if(max_x[x]<max_x[x*2])
t=x*2;
else t=x;
if(x*2+1<=left_size_x)
{
if(max_x[t]<max_x[x*2+1])
t=x*2+1;
}
if(t!=x)
{
p=max_x[t];
max_x[t]=max_x[x];
max_x[x]=p;
x=t;
}
else flag=1;
}
}
void shiftdownmax_y(long long int y)
{//y大根堆向下调整,不断地和自己的子节点来比较
long long int t,flag=0;
while(y*2<=left_size_y&&flag==0)
{
if(max_y[y]<max_y[y*2])
t=y*2;
else t=y;
if(y*2+1<=left_size_y)
{
if(max_y[t]<max_y[y*2+1])
t=y*2+1;
}
if(t!=y)
{
p=max_y[t];
max_y[t]=max_y[y];
max_y[y]=p;
y=t;
}
else flag=1;
}
}
void shiftdownmin_x(long long int x)
{//x小根堆向下调整,不断地和自己的子节点来比较
long long int t,flag=0;
while(x*2<=right_size_x&&flag==0)
{
if(min_x[x]>min_x[x*2])
t=x*2;
else t=x;
if(x*2+1<=right_size_x)
{
if(min_x[t]>min_x[x*2+1])
t=x*2+1;
}
if(t!=x)
{
p=min_x[t];
min_x[t]=min_x[x];
min_x[x]=p;
x=t;
}
else flag=1;
}
}
void shiftdownmin_y(long long int y)
{//y小根堆向下调整,不断地和自己的子节点来比较
long long int t,flag=0;
while(y*2<=right_size_y&&flag==0)
{
if(min_y[y]>min_y[y*2])
t=y*2;
else t=y;
if(y*2+1<=right_size_y)
{
if(min_y[t]>min_y[y*2+1])
t=y*2+1;
}
if(t!=y)
{
p=min_y[t];
min_y[t]=min_y[y];
min_y[y]=p;
y=t;
}
else flag=1;
}
}
void shiftupmax_x(long long int x)
{//x大根堆向上调整,不断地和父节点比较
long long int flag=0;
if(x==1) return;
while(x!=1&&flag==0)
{
if(max_x[x]>max_x[x/2])
{
p=max_x[x];
max_x[x]=max_x[x/2];
max_x[x/2]=p;
}
else flag=1;
x=x/2;
}
}
void shiftupmax_y(long long int y)
{//y大根堆向上调整,不断地和父节点比较
long long int flag=0;
if(y==1) return;
while(y!=1&&flag==0)
{
if(max_y[y]>max_y[y/2])
{
p=max_y[y];
max_y[y]=max_y[y/2];
max_y[y/2]=p;
}
else flag=1;
y=y/2;
}
}
void shiftupmin_x(long long int x)
{//x小根堆向上调整
long long int flag=0;
if(x==1) return;
while(x!=1 && flag==0)
{
if(min_x[x]<min_x[x/2])
{
p=min_x[x];
min_x[x]=min_x[x/2];
min_x[x/2]=p;
}
else flag=1;
x=x/2;
}
}
void shiftupmin_y(long long int y)
{//小根堆向上调整
long long int flag=0;
if(y==1) return;
while(y!=1 && flag==0)
{
if(min_y[y]<min_y[y/2])
{
p=min_y[y];
min_y[y]=min_y[y/2];
min_y[y/2]=p;
}
else flag=1;
y=y/2;
}
}
int main()
{
long long int dis_x,dis_y,dis;
long long int T=0;
scanf("%lld",&T);
for(long long int t=1;t<=T;t++)
{
printf("Case: %lld\n",t);
long long int n;//把所有的变量都弄成long long int ,以防万一
long long int now_x,now_y;
long int count_x,count_y;
scanf("%lld",&n);//初始化所有地变量
left_size_x=0; right_size_x=0;
left_size_y=0; right_size_y=0;//我的老哥,我才知道原来调试添加查看的变量还有上限,幸亏查出来了,就是这里初始化写错了
sum_max_x=0; sum_min_x=0;
sum_max_y=0; sum_min_y=0;
middle_x=0; middle_y=0;
count_x=0; count_y=0;
now_x=0; now_y=0;
dis_x=0;dis_y=0,dis=0;
for(long int i=0;i<n;i++)
{//很套路的算法了,先找中位数,比中位数大的放到小根堆,比中位数小的放到大根堆
if(i==0)
{
scanf("%lld",&middle_x);
scanf("%lld",&middle_y);
}
else
{
scanf("%lld",&now_x);
scanf("%lld",&now_y);
if(now_x>=middle_x)
{
min_x[++right_size_x]=now_x;
sum_min_x=sum_min_x+now_x;
shiftupmin_x(right_size_x);
}
else
{
max_x[++left_size_x]=now_x;
sum_max_x=sum_max_x+now_x;
shiftupmax_x(left_size_x);
}
count_x=left_size_x-right_size_x;
if(count_x>=2)//要是这俩的长度差距大于2 ,把middle放到少的那个堆里,把多的那个根节点弹出去,然后把最后的叶节点放到根的位置再向下排序
{
min_x[++right_size_x]=middle_x;
sum_min_x=sum_min_x+min_x[right_size_x];
shiftupmin_x(right_size_x);
middle_x=max_x[1];
sum_max_x=sum_max_x-max_x[1];
max_x[1]=max_x[left_size_x--];
shiftdownmax_x(1);
}
else if(count_x<=-2)
{
max_x[++left_size_x]=middle_x;
sum_max_x=sum_max_x+max_x[left_size_x];
shiftupmax_x(left_size_x);
middle_x=min_x[1];
sum_min_x=sum_min_x-min_x[1];
min_x[1]=min_x[right_size_x--];
shiftdownmin_x(1);
}
if(now_y>=middle_y)
{
min_y[++right_size_y]=now_y;
sum_min_y=sum_min_y+now_y;
shiftupmin_y(right_size_y);
}
else
{
max_y[++left_size_y]=now_y;
sum_max_y=sum_max_y+now_y;
shiftupmax_y(left_size_y);
}
count_y=left_size_y-right_size_y;
if(count_y>=2)
{
min_y[++right_size_y]=middle_y;
sum_min_y=sum_min_y+min_y[right_size_y];
shiftupmin_y(right_size_y);
middle_y=max_y[1];
sum_max_y=sum_max_y-max_y[1];
max_y[1]=max_y[left_size_y--];
shiftdownmax_y(1);
}
else if(count_y<=-2)
{
max_y[++left_size_y]=middle_y;
sum_max_y=sum_max_y+max_y[left_size_y];
shiftupmax_y(left_size_y);
middle_y=min_y[1];
sum_min_y=sum_min_y-min_y[1];
min_y[1]=min_y[right_size_y--];
shiftdownmin_y(1);
}
}
dis_x=sum_min_x-middle_x*right_size_x+middle_x*left_size_x-sum_max_x;
dis_y=sum_min_y-middle_y*right_size_y+middle_y*left_size_y-sum_max_y;//用公式直接计算距离
dis=dis_x+dis_y;
printf("%lld.0000\n",dis);
}
}
return 0;
}